diff --git a/__init__.py b/__init__.py index f4ae4f8..6712079 100644 --- a/__init__.py +++ b/__init__.py @@ -9,11 +9,11 @@ __version__ = "1.2.3" -from . import _perlin, _simplex - +from . import _perlin, _simplex, _cellular snoise2 = _simplex.noise2 snoise3 = _simplex.noise3 snoise4 = _simplex.noise4 pnoise1 = _perlin.noise1 pnoise2 = _perlin.noise2 pnoise3 = _perlin.noise3 +cnoise2 = _cellular.noise2 \ No newline at end of file diff --git a/_cellular.c b/_cellular.c new file mode 100644 index 0000000..690c951 --- /dev/null +++ b/_cellular.c @@ -0,0 +1,100 @@ +// Copyright (c) 2008, Casey Duncan (casey dot duncan at gmail dot com) +// see LICENSE.txt for details +// $Id$ + +#include "Python.h" +#include "_noise.h" +#include + +#define PERM_SIZE 256 +#define PERM_MASK (PERM_SIZE - 1) +#define OFFSET_INV 0.0625f /* 1.0 / 16.0 */ + +static inline float sqr_dist(float x1, float y1, float x2, float y2) { + return ((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)); +} + +// cellular noise implementation +// https://en.wikipedia.org/wiki/Worley_noise +// https://sangillee.com/2025-04-18-cellular-noises/ +float noise2_cellular(float x, float y) { + int ix = fastfloor(x); + int iy = fastfloor(y); + + float xf = x - (float)ix; + float yf = y - (float)iy; + float min_dist_sq = 8.0f; + + int i_wrapped[3], j_wrapped[3]; + for (int n = 0; n < 3; n++) { + i_wrapped[n] = (ix + (n - 1) + 1024) & PERM_MASK; + j_wrapped[n] = (iy + (n - 1) + 1024) & PERM_MASK; + } + + for (int i = 0; i < 3; i++) { + float dx_base = (float)(i - 1) - xf; + int o = PERM[i_wrapped[i]]; + for (int j = 0; j < 3; j++) { + int hash = PERM[o + j_wrapped[j]]; + float dx = dx_base + (float)(hash & 15) * OFFSET_INV; + float dy = ((float)(j - 1) - yf) + (float)(hash >> 4) * OFFSET_INV; + + float d_sq = (dx * dx) + (dy * dy); + + if (d_sq < min_dist_sq) { + min_dist_sq = d_sq; + } + } + } + return sqrtf(min_dist_sq); +} + +static PyObject *py_noise2_cellular(PyObject *self, PyObject *args) { + float x, y; + if (!PyArg_ParseTuple(args, "ff", &x, &y)) + return NULL; + return PyFloat_FromDouble((double)noise2_cellular(x, y)); +} + +PyDoc_STRVAR(noise2_doc, + "noise2(x, y)\n\n" + "2 dimensional cellular (Worley) noise function.\n" + "Returns the distance to the nearest feature point."); + +static PyMethodDef cellular_functions[] = { + {"noise2", (PyCFunction) py_noise2_cellular, METH_VARARGS | METH_KEYWORDS, noise2_doc}, + {NULL} +}; + +PyDoc_STRVAR(module_doc, "Native-code implementation of cellular (Worley) noise functions"); + + +#if PY_MAJOR_VERSION >= 3 + +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "_cellular", + module_doc, + -1, /* m_size */ + cellular_functions, /* m_methods */ + NULL, /* m_reload (unused) */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL /* m_free */ +}; + +PyObject * +PyInit__cellular(void) +{ + return PyModule_Create(&moduledef); +} + +#else + +void +init_cellular(void) +{ + Py_InitModule3("_cellular", cellular_functions, module_doc); +} + +#endif \ No newline at end of file diff --git a/examples/cellnoise.py b/examples/cellnoise.py new file mode 100644 index 0000000..2285282 --- /dev/null +++ b/examples/cellnoise.py @@ -0,0 +1,33 @@ +"""Writes a 256x256 grayscale cell noise texture to a file in pgm format +(see http://netpbm.sourceforge.net/doc/pgm.html) +""" +# $Id: cellnoise.py $ + +import sys +from noise import _cellular + +if len(sys.argv) not in (2, 3) or '--help' in sys.argv or '-h' in sys.argv: + print() + print(__doc__) + raise SystemExit + +if len(sys.argv) < 2: + print('Usage: python cellular_texture.py OUTPUT_FILE.pgm') + sys.exit(1) + +filename = sys.argv[1] +size = 256 +scale = 16.0 + +with open(filename, 'wt') as f: + f.write('P2\n') + f.write(f'{size} {size}\n') + f.write('255\n') + + for y in range(size): + for x in range(size): + val = _cellular.noise2(x / scale, y / scale) + brightness = int((1.0 - min(val, 1.0)) * 255) + f.write(f"{brightness}\n") + +print(f"Texture saved to {filename}") \ No newline at end of file diff --git a/setup.py b/setup.py index ccfafdf..758b128 100644 --- a/setup.py +++ b/setup.py @@ -64,6 +64,9 @@ ), Extension('noise._perlin', ['_perlin.c'], extra_compile_args=compile_args, + ), + Extension('noise._cellular', ['_cellular.c'], + extra_compile_args=compile_args, ) ], )