GithubHelp home page GithubHelp logo

lmas / opensimplex Goto Github PK

View Code? Open in Web Editor NEW
237.0 9.0 28.0 707 KB

This repo has been migrated to https://code.larus.se/lmas/opensimplex

License: MIT License

Python 98.73% Makefile 1.27%
opensimplex perlin-noise noise python simplex opensimplex-noise

opensimplex's Introduction

Migrated

This repo has been migrated to https://code.larus.se/lmas/opensimplex.

OpenSimplex Noise

build-status pypi-version pypi-downloads

OpenSimplex is a noise generation function like Perlin or Simplex noise, but better.

OpenSimplex noise is an n-dimensional gradient noise function that was
developed in order to overcome the patent-related issues surrounding
Simplex noise, while continuing to also avoid the visually-significant
directional artifacts characteristic of Perlin noise.
- Kurt Spencer

This is merely a python port of Kurt Spencer's original code (released to the public domain) and neatly wrapped up in a package.

Status

The master branch contains the latest code (possibly unstable), with automatic tests running for Python 3.8, 3.9, 3.10 on Linux, MacOS and Windows. FreeBSD is also supported, though it's only locally tested as Github Actions don't offer FreeBSD support.

Please refer to the version tags for the latest stable version.

Updates for v0.4+:

  • Adds a hard dependency on 'Numpy', for array optimizations aimed at heavier workloads.
  • Adds optional dependency on 'Numba', for further speed optimizations using caching (currently untested due to issues with llvmlite).
  • Adds typing support.
  • General refactor and cleanup of the library, tests and docs.
  • Breaking changes: API functions uses new names.

Contributions

Bug reports, bug fixes and other issues with existing features of the library are welcomed and will be handled during the maintainer's free time. New stand-alone examples are also accepted.

However, pull requests with new features for the core internals will not be accepted as it eats up too much weekend time, which I would rather spend on library stability instead.

Usage

Installation

pip install opensimplex

Basic usage

>>> import opensimplex
>>> opensimplex.seed(1234)
>>> n = opensimplex.noise2(x=10, y=10)
>>> print(n)
0.580279369186297

Running tests and benchmarks

Setup a development environment:

make dev
source devenv/bin/activate
make deps

And then run the tests:

make test

Or the benchmarks:

make benchmark

For more advanced examples, see the files in the tests and examples directories.

API

opensimplex.seed(seed)

Seeds the underlying permutation array (which produces different outputs),
using a 64-bit integer number.
If no value is provided, a static default will be used instead.

seed(13)

random_seed()

Works just like seed(), except it uses the system time (in ns) as a seed value.
Not guaranteed to be random so use at your own risk.

random_seed()

get_seed()

Return the value used to seed the initial state.
:return: seed as integer

>>> get_seed()
3

opensimplex.noise2(x, y)

Generate 2D OpenSimplex noise from X,Y coordinates.
:param x: x coordinate as float
:param y: y coordinate as float
:return:  generated 2D noise as float, between -1.0 and 1.0

>>> noise2(0.5, 0.5)
-0.43906247097569345

opensimplex.noise2array(x, y)

Generates 2D OpenSimplex noise using Numpy arrays for increased performance.
:param x: numpy array of x-coords
:param y: numpy array of y-coords
:return:  2D numpy array of shape (y.size, x.size) with the generated noise
          for the supplied coordinates

>>> rng = numpy.random.default_rng(seed=0)
>>> ix, iy = rng.random(2), rng.random(2)
>>> noise2array(ix, iy)
array([[ 0.00449931, -0.01807883],
       [-0.00203524, -0.02358477]])

opensimplex.noise3(x, y, z)

Generate 3D OpenSimplex noise from X,Y,Z coordinates.
:param x: x coordinate as float
:param y: y coordinate as float
:param z: z coordinate as float
:return:  generated 3D noise as float, between -1.0 and 1.0

>>> noise3(0.5, 0.5, 0.5)
0.39504955501618155

opensimplex.noise3array(x, y, z)

Generates 3D OpenSimplex noise using Numpy arrays for increased performance.
:param x: numpy array of x-coords
:param y: numpy array of y-coords
:param z: numpy array of z-coords
:return:  3D numpy array of shape (z.size, y.size, x.size) with the generated
          noise for the supplied coordinates

>>> rng = numpy.random.default_rng(seed=0)
>>> ix, iy, iz = rng.random(2), rng.random(2), rng.random(2)
>>> noise3array(ix, iy, iz)
array([[[0.54942818, 0.54382411],
        [0.54285204, 0.53698967]],
       [[0.48107672, 0.4881196 ],
        [0.45971748, 0.46684901]]])

opensimplex.noise4(x, y, z, w)

Generate 4D OpenSimplex noise from X,Y,Z,W coordinates.
:param x: x coordinate as float
:param y: y coordinate as float
:param z: z coordinate as float
:param w: w coordinate as float
:return:  generated 4D noise as float, between -1.0 and 1.0

>>> noise4(0.5, 0.5, 0.5, 0.5)
0.04520359600370195

opensimplex.noise4array(x, y, z, w)

Generates 4D OpenSimplex noise using Numpy arrays for increased performance.
:param x: numpy array of x-coords
:param y: numpy array of y-coords
:param z: numpy array of z-coords
:param w: numpy array of w-coords
:return:  4D numpy array of shape (w.size, z.size, y.size, x.size) with the
          generated noise for the supplied coordinates

>>> rng = numpy.random.default_rng(seed=0)
>>> ix, iy, iz, iw = rng.random(2), rng.random(2), rng.random(2), rng.random(2)
>>> noise4array(ix, iy, iz, iw)
array([[[[0.30334626, 0.29860705],
         [0.28271858, 0.27805178]],
        [[0.26601215, 0.25305428],
         [0.23387872, 0.22151356]]],
       [[[0.3392759 , 0.33585534],
         [0.3343468 , 0.33118285]],
        [[0.36930335, 0.36046537],
         [0.36360679, 0.35500328]]]])

FAQ

  • What does the distribution of the noise values look like?

Noise Distribution

  • Is this relevantly different enough to avoid any real trouble with the original patent?

    If you read the patent claims:

    Claim #1 talks about the hardware-implementation-optimized gradient generator. Most software implementations of Simplex Noise don't use this anyway, and OpenSimplex Noise certainly doesn't.

    Claim #2(&3&4) talk about using (x',y',z')=(x+s,y+s,z+s) where s=(x+y+z)/3 to transform the input (render space) coordinate onto a simplical grid, with the intention to make all of the "scissor-simplices" approximately regular. OpenSimplex Noise (in 3D) uses s=-(x+y+z)/6 to transform the input point to a point on the Simplectic honeycomb lattice so that the simplices bounding the (hyper)cubes at (0,0,..,0) and (1,1,...,1) work out to be regular. It then mathematically works out that s=(x+y+z)/3 is needed for the inverse transform, but that's performing a different (and opposite) function.

    Claim #5(&6) are specific to the scissor-simplex lattice. Simplex Noise divides the (squashed) n-dimensional (hyper)cube into n! simplices based on ordered edge traversals, whereas OpenSimplex Noise divides the (stretched) n-dimensional (hyper)cube into n polytopes (simplices, rectified simplices, birectified simplices, etc.) based on the separation (hyper)planes at integer values of (x'+y'+z'+...).

    Another interesting point is that, if you read all of the claims, none of them appear to apply to the 2D analogue of Simplex noise so long as it uses a gradient generator separate from the one described in claim #1. The skew function in Claim #2 only applies to 3D, and #5 explicitly refers to n>=3.

    And none of the patent claims speak about using surflets / "spherically symmetric kernels" to generate the "images with texture that do not have visible grid artifacts," which is probably the biggest similarity between the two algorithms.

Credits

  • Kurt Spencer - Original work
  • Owen Raccuglia - Test cases, Go Module
  • /u/redblobgames - Fixed conversion for Java's long type, see Reddit

And all the other Github Contributors and Bug Hunters. Thanks!

License

While the original work was released to the public domain by Kurt, this package is using the MIT license.

Please see the file LICENSE for details.

Example Output

More example code and trinkets can be found in the examples directory.

Example images visualising 2D, 3D and 4D noise on a 2D plane, using the default seed:

2D noise

Noise 2D

3D noise

Noise 3D

4D noise

Noise 4D

opensimplex's People

Contributors

creamycookie avatar danielshrimpton avatar dependabot[bot] avatar julian-wyatt avatar kdotjpg avatar lmas avatar rugnirviking avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

opensimplex's Issues

How to replicate tests using noiseXarray?

How do I replicate the 3d noise test using the noise3array function?

I'm trying to do this, but the result is not the same.

rng = np.random.default_rng(seed=12)
ix, iy, iz = rng.random(X), rng.random(Y), rng.random(Z)
noise = simplex.noise3array(ix, iy, iz)
noise = np.int32((noise + 1) * 128)

Limited documentation / example code. Trying to understand how to use numpy arrays efficiently.

from opensimplex import OpenSimplex
import numpy as np

class TerrainGenerator:
	def __init__(self, game):
		self.game = game
		print(f"Using seed {self.game.seed}")
		self.generator = OpenSimplex(seed=(self.game.seed)).noise2

	def get_heightmap(self,x,z):
		height = self.generator(x,z)
		return height

	def get_array_heightmap(self,x,width,z,depth):
		out = np.zeros((width, depth), dtype=float)
		gen = OpenSimplex(seed=self.game.seed).noise2array #(arr, arr)
		z_values = np.arange(depth) + z
		x_values = np.full((width), x, dtype=float)
		for w in range(width):
			out[w, : ] = gen(z_values, x_values)
			if not w == width - 1:
				x_values = x_values + 1
		return out

	# def get_array_heightmap(self,x,width,z,depth): #Slower
	# 	# out = np.zeros((width, depth), dtype=float)
	# 	gen = OpenSimplex(seed=self.game.seed).noise2array #(arr, arr)
	# 	z_values = np.arange(z, depth+z)
	# 	z_values = np.repeat(z_values,width,axis=0)
	# 	x_values = np.arange(x, width+x)
	# 	x_values = np.resize(x_values,width*width)
	# 	# x_values = np.hstack((x_values, ) * depth)
	# 	out = gen(x_values,z_values).reshape(width,depth)
	# 	return out

if __name__ == "__main__":
	from time import perf_counter

	class dummy_game:
		def __init__(self):
			self.seed = 10

	SIZE = 100

	generator = TerrainGenerator(dummy_game()) #20 seconds
	print("Using old method")
	arr = np.zeros((SIZE,SIZE), dtype=float)
	t1 = perf_counter()
	for x in range(SIZE):
		for z in range(SIZE):
			arr[z][x] = generator.get_heightmap(x,z)
	t2 = perf_counter()
	print(len(arr), len(arr[0]))
	print(arr[:4,:4])
	print("Old method took - ", t2-t1)  #~0.19 #Faster weirdly... 

	print("Using new method")
	t1 = perf_counter()
	x,z = 0,0
	arr = generator.get_array_heightmap(x,SIZE,z,SIZE)
	t2 = perf_counter()
	print(len(arr), len(arr[0]))
	print(arr[:4,:4])
	print("New method took - ", t2-t1)  #~0.28

I am having a very hard time understanding how to implement the noise2array more efficiently than the standard noise2 method.
I have tried several different ways to populate the numpy arrays, my goal is to generate terrain in 100x100 unit chunks using the noise as a heightmap. Improved documentation / sample code in the repository would likely be appreciated by a number of users.

black causes lint errors for pycodestyle

# make lint
pycodestyle --max-line-length 120 opensimplex
opensimplex/opensimplex.py:78:30: E203 whitespace before ':'
opensimplex/opensimplex.py:85:34: E203 whitespace before ':'
opensimplex/opensimplex.py:92:38: E203 whitespace before ':'
*** Error code 1

See Black FAQ and pycodestyle issue.
Nothing to be done now.

Conda package

Hi,

it would be nice, to have opensimplex on conda-forge. I wanted to create a recipe there but noticed, that I can't run the tests due to #41 .

Are you ok with me creating a recipe? I would add you as a maintainer there, so you get access.

Cheers,
Sebastian

GPU support

Hi, Thanks for the great work!

Im trying to integrate the simplex noise into my pytorch pipeline, but it is not gpt-supported and i have to loop hundred of thousand times to create noise for my img, any idea how to accelerate it in pytorch?

Numba/Numpy version

I've implemented a fairly direct port/optimization using Numba for python. This runs ~10x faster for single point noise calculations, and 50-100x faster for calculating noise arrays. Obviously, this requires installation of the Numba library, but people are free to check it out if they're interested.

Partial differential function

If this function is smooth and differentiable, then a partial differential in each of the three axes would be a useful feature.

I've seen this simplex noise function used for generate blobby surfaces by plotting the set of points where f(x,y,z)=0. Knowing the differential at that each point would mean we would know the normal of the surface without needing to sample the function six times. It could also be useful for benchmarking various scipy.optimize.minimize() which require this differential function.

This challenge is for a mathematician with too much spare time on their hands.

Unable to install version 0.4.5

I am unable to install version 0.4.5

pip install .\opensimplex-0.4.5.zip
Processing c:\users\derp\downloads\opensimplex-0.4.5.zip
Installing build dependencies ... done
Getting requirements to build wheel ... error
error: subprocess-exited-with-error

× Getting requirements to build wheel did not run successfully.
│ exit code: 1
╰─> [27 lines of output]
Traceback (most recent call last):
File "C:\Users\Derp\AppData\Local\Programs\Python\Python311\Lib\site-packages\pip_vendor\pyproject_hooks_in_process_in_process.py", line 353, in
main()
File "C:\Users\Derp\AppData\Local\Programs\Python\Python311\Lib\site-packages\pip_vendor\pyproject_hooks_in_process_in_process.py", line 335, in main
json_out['return_val'] = hook(**hook_input['kwargs'])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Derp\AppData\Local\Programs\Python\Python311\Lib\site-packages\pip_vendor\pyproject_hooks_in_process_in_process.py", line 118, in get_requires_for_build_wheel
return hook(config_settings)
^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Derp\AppData\Local\Temp\pip-build-env-xj4mpq9_\overlay\Lib\site-packages\setuptools\build_meta.py", line 341, in get_requires_for_build_wheel
return self.get_build_requires(config_settings, requirements=['wheel'])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Derp\AppData\Local\Temp\pip-build-env-xj4mpq9
\overlay\Lib\site-packages\setuptools\build_meta.py", line 323, in get_build_requires
self.run_setup()
File "C:\Users\Derp\AppData\Local\Temp\pip-build-env-xj4mpq9
\overlay\Lib\site-packages\setuptools\build_meta.py", line 488, in run_setup
self).run_setup(setup_script=setup_script)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\Derp\AppData\Local\Temp\pip-build-env-xj4mpq9_\overlay\Lib\site-packages\setuptools\build_meta.py", line 338, in run_setup
exec(code, locals())
File "", line 4, in
File "C:\Users\Derp\AppData\Local\Temp\pip-req-build-7enj910b\opensimplex_init_.py", line 4, in
from .api import *
File "C:\Users\Derp\AppData\Local\Temp\pip-req-build-7enj910b\opensimplex\api.py", line 1, in
from .constants import np
File "C:\Users\Derp\AppData\Local\Temp\pip-req-build-7enj910b\opensimplex\constants.py", line 1, in
import numpy as np
ModuleNotFoundError: No module named 'numpy'
[end of output]

note: This error originates from a subprocess, and is likely not a problem with pip.
error: subprocess-exited-with-error

× Getting requirements to build wheel did not run successfully.
│ exit code: 1
╰─> See above for output.

note: This error originates from a subprocess, and is likely not a problem with pip.

This is despite having numpy installed:

pip install numpy
Requirement already satisfied: numpy in c:\users\derp\appdata\local\programs\python\python311\lib\site-packages (1.25.1)

https://pypi.org/project/opensimplex/ lists the latest version as 0.4.4

Is 0.4.5 not official or has something gone wrong?

A small issue regarding noiseXarray

>>> n=simplex.noise3array(x=10, y=10, z=10)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python39\lib\site-packages\opensimplex\opensimplex.py", line 34, in noise3array
    return _noise3a(x, y, z, self._perm, self._perm_grad_index3)
  File "C:\Python39\lib\site-packages\opensimplex\opensimplex.py", line 111, in _noise3a
    noise = np.zeros(x.size, dtype=np.double)
AttributeError: 'int' object has no attribute 'size'

Perhaps a different attribute needs to be used?

version .04 docs

import opensimplex
opensimplex.seed(1234)
n = opensimplex.noise2(x=10, y=10)
print(n)
0.580279369186297

This doesn't work since the "module object is not callable"

I find the old method works (except for the change of function name):

from opensimplex import OpenSimplex
simplex=OpenSimplex(seed=1234)
n = simplex.noise2(x=10, y=10)

opensimplex2

  • Wait for KdotJPG to finish his FastNoiseLite work
  • Research and summarize improvements
  • Upgrade source
  • Add numpy optimizations per #4
  • Investigate maintaining multiple versions and pypi releases (numpy optimized or not, opensimplex2S or 2F) Hmm nope, see #4
  • Normalize function names in all versions (including v3, which breaks compatibility?!)
  • Document output values from multiple sources (python, go, C#)?
  • Inspect output image quality
  • Fix the tests (gonna need a whole new suite of tests..)
  • Update README (breaking name changes, v0.3 deprecrated, new imgs, examples, clarify it's opensimplex2 but lib is v0.4)
  • Release v0.4-pre (with warnings about breaking changes?)

Publish wheels on PyPI

Hi,

Thanks for making this great library! It would be awesome if there could be wheels built and uploaded to PyPI. This is as easy as:

python setup.py bdist_wheel
twine upload dist/opensimplex-0.3-py3-none-any.whl

Certain packaging/distribution tools require wheels to be available, since wheels do not require additional build steps and are easier to work with.

Different values on different os

Hi. I'm bad speak on eng, sry. But your program return different values on Windows and MacOS.
I use c_int64 instead c_long. And fix this issue. Mb I'm dumb, help me) for right solve

Range of noise result

Hi, I was wondering what the range of the nose functions are. It doesn't say that anywhere in the documentation, and I am not reaching values above 0.8

3D noise repeats itself

Maybe this is just the way the algorithm works, but neither 2D nor 4D noise repeat themselves, so it seems odd to me.

3d is repetitive Figure_0

tmp = OpenSimplex()
N = 100_000
x_max = 5000
t = np.linspace(0, x_max, N)
for x in t:
    out_2d.append(tmp.noise2d(x=x, y=y))
    out_3d.append(tmp.noise3d(x=x, y=y, z=0))
    out_4d.append(tmp.noise4d(x=x, y=y, z=0, w=0))

Question about noiseXarray behavior

Hello! I'm trying to use the array functions to get 2d noise, but the output is unexpected. I'm providing the x and y arrays, say for a 3x3 grid, x = [0, 1, 2], y = [0, 1, 2] and I would expect that it would return a 2D array of the noise, so that I can then access coordinates with noise_result[x][y], but instead it returns a 1D array the length of x. Do I need to manually insert each x/y combination into the input arrays like x = [0, 1, 2, 0, 1, 2, 0, 1, 2], y = [0, 0, 0, 1, 1, 1, 2, 2, 2], then do something like noise_result[x * x_size + y]?

tests from sdist not working

Hi there,

the tests included in sdist are not working, since not all files are included and one gets the following error:

FileNotFoundError: [Errno 2] No such file or directory: 'tests/numpy_shapes.npz'
...
FileNotFoundError: [Errno 2] No such file or directory: 'tests/samples.json.gz'

So I think, these two files need to be included explicitly in the sdist.

Cheers,
Sebastian

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.