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

Random Seed as Parameter & Added Res Validity Check #7

Open
wants to merge 3 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
80 changes: 57 additions & 23 deletions perlin_numpy/perlin2d.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import numpy as np

from warnings import warn


def interpolant(t):
return t*t*t*(t*(t*6 - 15) + 10)
return t * t * t * (t * (t * 6 - 15) + 10)


def check_res(res, shape):
for dim, dim_res in enumerate(res):
assert (
shape[dim] % dim_res == 0
), f"Dimension {dim} has a length of {shape[dim]} which can not be divided using the resolution specified: {dim_res}"

return


def generate_perlin_noise_2d(
shape, res, tileable=(False, False), interpolant=interpolant
shape, res, tileable=(False, False), interpolant=interpolant, seed=None
):
"""Generate a 2D numpy array of perlin noise.

Expand All @@ -27,38 +38,57 @@ def generate_perlin_noise_2d(
Raises:
ValueError: If shape is not a multiple of res.
"""
check_res(res, shape)

delta = (res[0] / shape[0], res[1] / shape[1])
d = (shape[0] // res[0], shape[1] // res[1])
grid = np.mgrid[0:res[0]:delta[0], 0:res[1]:delta[1]]\
.transpose(1, 2, 0) % 1

grid = np.mgrid[0 : res[0] : delta[0], 0 : res[1] : delta[1]].transpose(1, 2, 0) % 1

# Gradients
angles = 2*np.pi*np.random.rand(res[0]+1, res[1]+1)
if seed is not None:
np.random.seed(seed)

angles = 2 * np.pi * np.random.rand(res[0] + 1, res[1] + 1)
gradients = np.dstack((np.cos(angles), np.sin(angles)))

if tileable[0]:
gradients[-1,:] = gradients[0,:]
gradients[-1, :] = gradients[0, :]
if tileable[1]:
gradients[:,-1] = gradients[:,0]
gradients[:, -1] = gradients[:, 0]

gradients = gradients.repeat(d[0], 0).repeat(d[1], 1)
g00 = gradients[ :-d[0], :-d[1]]
g10 = gradients[d[0]: , :-d[1]]
g01 = gradients[ :-d[0],d[1]: ]
g11 = gradients[d[0]: ,d[1]: ]

g00 = gradients[: -d[0], : -d[1]]
g10 = gradients[d[0] :, : -d[1]]
g01 = gradients[: -d[0], d[1] :]
g11 = gradients[d[0] :, d[1] :]

# Ramps
n00 = np.sum(np.dstack((grid[:,:,0] , grid[:,:,1] )) * g00, 2)
n10 = np.sum(np.dstack((grid[:,:,0]-1, grid[:,:,1] )) * g10, 2)
n01 = np.sum(np.dstack((grid[:,:,0] , grid[:,:,1]-1)) * g01, 2)
n11 = np.sum(np.dstack((grid[:,:,0]-1, grid[:,:,1]-1)) * g11, 2)
n00 = np.sum(np.dstack((grid[:, :, 0], grid[:, :, 1])) * g00, 2)
n10 = np.sum(np.dstack((grid[:, :, 0] - 1, grid[:, :, 1])) * g10, 2)
n01 = np.sum(np.dstack((grid[:, :, 0], grid[:, :, 1] - 1)) * g01, 2)
n11 = np.sum(np.dstack((grid[:, :, 0] - 1, grid[:, :, 1] - 1)) * g11, 2)

# Interpolation
t = interpolant(grid)
n0 = n00*(1-t[:,:,0]) + t[:,:,0]*n10
n1 = n01*(1-t[:,:,0]) + t[:,:,0]*n11
return np.sqrt(2)*((1-t[:,:,1])*n0 + t[:,:,1]*n1)
n0 = n00 * (1 - t[:, :, 0]) + t[:, :, 0] * n10
n1 = n01 * (1 - t[:, :, 0]) + t[:, :, 0] * n11

noise = np.sqrt(2) * ((1 - t[:, :, 1]) * n0 + t[:, :, 1] * n1)

return noise


def generate_fractal_noise_2d(
shape, res, octaves=1, persistence=0.5,
lacunarity=2, tileable=(False, False),
interpolant=interpolant
shape,
res,
octaves=1,
persistence=0.5,
lacunarity=2,
tileable=(False, False),
interpolant=interpolant,
seed=None,
):
"""Generate a 2D numpy array of fractal noise.

Expand Down Expand Up @@ -87,10 +117,14 @@ def generate_fractal_noise_2d(
noise = np.zeros(shape)
frequency = 1
amplitude = 1

for _ in range(octaves):
noise += amplitude * generate_perlin_noise_2d(
shape, (frequency*res[0], frequency*res[1]), tileable, interpolant
octave_noise = generate_perlin_noise_2d(
shape, (frequency * res[0], frequency * res[1]), tileable, interpolant, seed
)

noise += amplitude * octave_noise
frequency *= lacunarity
amplitude *= persistence

return noise
138 changes: 98 additions & 40 deletions perlin_numpy/perlin3d.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import numpy as np

from .perlin2d import interpolant
from .perlin2d import interpolant, check_res


def generate_perlin_noise_3d(
shape, res, tileable=(False, False, False),
interpolant=interpolant
shape, res, tileable=(False, False, False), interpolant=interpolant, seed=None
):
"""Generate a 3D numpy array of perlin noise.

Expand All @@ -26,56 +25,110 @@ def generate_perlin_noise_3d(
Raises:
ValueError: If shape is not a multiple of res.
"""
check_res(res, shape)

delta = (res[0] / shape[0], res[1] / shape[1], res[2] / shape[2])
d = (shape[0] // res[0], shape[1] // res[1], shape[2] // res[2])
grid = np.mgrid[0:res[0]:delta[0],0:res[1]:delta[1],0:res[2]:delta[2]]
grid = np.mgrid[0:res[0]:delta[0],0:res[1]:delta[1],0:res[2]:delta[2]]

grid = np.mgrid[0 : res[0] : delta[0], 0 : res[1] : delta[1], 0 : res[2] : delta[2]]
grid = np.mgrid[0 : res[0] : delta[0], 0 : res[1] : delta[1], 0 : res[2] : delta[2]]
grid = grid.transpose(1, 2, 3, 0) % 1

# Gradients
theta = 2*np.pi*np.random.rand(res[0] + 1, res[1] + 1, res[2] + 1)
phi = 2*np.pi*np.random.rand(res[0] + 1, res[1] + 1, res[2] + 1)
if seed is not None:
np.random.seed(seed)

theta = 2 * np.pi * np.random.rand(res[0] + 1, res[1] + 1, res[2] + 1)
phi = 2 * np.pi * np.random.rand(res[0] + 1, res[1] + 1, res[2] + 1)

gradients = np.stack(
(np.sin(phi)*np.cos(theta), np.sin(phi)*np.sin(theta), np.cos(phi)),
axis=3
(np.sin(phi) * np.cos(theta), np.sin(phi) * np.sin(theta), np.cos(phi)), axis=3
)

if tileable[0]:
gradients[-1,:,:] = gradients[0,:,:]
gradients[-1, :, :] = gradients[0, :, :]
if tileable[1]:
gradients[:,-1,:] = gradients[:,0,:]
gradients[:, -1, :] = gradients[:, 0, :]
if tileable[2]:
gradients[:,:,-1] = gradients[:,:,0]
gradients[:, :, -1] = gradients[:, :, 0]

gradients = gradients.repeat(d[0], 0).repeat(d[1], 1).repeat(d[2], 2)
g000 = gradients[ :-d[0], :-d[1], :-d[2]]
g100 = gradients[d[0]: , :-d[1], :-d[2]]
g010 = gradients[ :-d[0],d[1]: , :-d[2]]
g110 = gradients[d[0]: ,d[1]: , :-d[2]]
g001 = gradients[ :-d[0], :-d[1],d[2]: ]
g101 = gradients[d[0]: , :-d[1],d[2]: ]
g011 = gradients[ :-d[0],d[1]: ,d[2]: ]
g111 = gradients[d[0]: ,d[1]: ,d[2]: ]

g000 = gradients[: -d[0], : -d[1], : -d[2]]
g100 = gradients[d[0] :, : -d[1], : -d[2]]
g010 = gradients[: -d[0], d[1] :, : -d[2]]
g110 = gradients[d[0] :, d[1] :, : -d[2]]
g001 = gradients[: -d[0], : -d[1], d[2] :]
g101 = gradients[d[0] :, : -d[1], d[2] :]
g011 = gradients[: -d[0], d[1] :, d[2] :]
g111 = gradients[d[0] :, d[1] :, d[2] :]

# Ramps
n000 = np.sum(np.stack((grid[:,:,:,0] , grid[:,:,:,1] , grid[:,:,:,2] ), axis=3) * g000, 3)
n100 = np.sum(np.stack((grid[:,:,:,0]-1, grid[:,:,:,1] , grid[:,:,:,2] ), axis=3) * g100, 3)
n010 = np.sum(np.stack((grid[:,:,:,0] , grid[:,:,:,1]-1, grid[:,:,:,2] ), axis=3) * g010, 3)
n110 = np.sum(np.stack((grid[:,:,:,0]-1, grid[:,:,:,1]-1, grid[:,:,:,2] ), axis=3) * g110, 3)
n001 = np.sum(np.stack((grid[:,:,:,0] , grid[:,:,:,1] , grid[:,:,:,2]-1), axis=3) * g001, 3)
n101 = np.sum(np.stack((grid[:,:,:,0]-1, grid[:,:,:,1] , grid[:,:,:,2]-1), axis=3) * g101, 3)
n011 = np.sum(np.stack((grid[:,:,:,0] , grid[:,:,:,1]-1, grid[:,:,:,2]-1), axis=3) * g011, 3)
n111 = np.sum(np.stack((grid[:,:,:,0]-1, grid[:,:,:,1]-1, grid[:,:,:,2]-1), axis=3) * g111, 3)
n000 = np.sum(
np.stack((grid[:, :, :, 0], grid[:, :, :, 1], grid[:, :, :, 2]), axis=3) * g000,
3,
)
n100 = np.sum(
np.stack((grid[:, :, :, 0] - 1, grid[:, :, :, 1], grid[:, :, :, 2]), axis=3)
* g100,
3,
)
n010 = np.sum(
np.stack((grid[:, :, :, 0], grid[:, :, :, 1] - 1, grid[:, :, :, 2]), axis=3)
* g010,
3,
)
n110 = np.sum(
np.stack((grid[:, :, :, 0] - 1, grid[:, :, :, 1] - 1, grid[:, :, :, 2]), axis=3)
* g110,
3,
)
n001 = np.sum(
np.stack((grid[:, :, :, 0], grid[:, :, :, 1], grid[:, :, :, 2] - 1), axis=3)
* g001,
3,
)
n101 = np.sum(
np.stack((grid[:, :, :, 0] - 1, grid[:, :, :, 1], grid[:, :, :, 2] - 1), axis=3)
* g101,
3,
)
n011 = np.sum(
np.stack((grid[:, :, :, 0], grid[:, :, :, 1] - 1, grid[:, :, :, 2] - 1), axis=3)
* g011,
3,
)
n111 = np.sum(
np.stack(
(grid[:, :, :, 0] - 1, grid[:, :, :, 1] - 1, grid[:, :, :, 2] - 1), axis=3
)
* g111,
3,
)

# Interpolation
t = interpolant(grid)
n00 = n000*(1-t[:,:,:,0]) + t[:,:,:,0]*n100
n10 = n010*(1-t[:,:,:,0]) + t[:,:,:,0]*n110
n01 = n001*(1-t[:,:,:,0]) + t[:,:,:,0]*n101
n11 = n011*(1-t[:,:,:,0]) + t[:,:,:,0]*n111
n0 = (1-t[:,:,:,1])*n00 + t[:,:,:,1]*n10
n1 = (1-t[:,:,:,1])*n01 + t[:,:,:,1]*n11
return ((1-t[:,:,:,2])*n0 + t[:,:,:,2]*n1)
n00 = n000 * (1 - t[:, :, :, 0]) + t[:, :, :, 0] * n100
n10 = n010 * (1 - t[:, :, :, 0]) + t[:, :, :, 0] * n110
n01 = n001 * (1 - t[:, :, :, 0]) + t[:, :, :, 0] * n101
n11 = n011 * (1 - t[:, :, :, 0]) + t[:, :, :, 0] * n111
n0 = (1 - t[:, :, :, 1]) * n00 + t[:, :, :, 1] * n10
n1 = (1 - t[:, :, :, 1]) * n01 + t[:, :, :, 1] * n11

noise = (1 - t[:, :, :, 2]) * n0 + t[:, :, :, 2] * n1

return noise


def generate_fractal_noise_3d(
shape, res, octaves=1, persistence=0.5, lacunarity=2,
tileable=(False, False, False), interpolant=interpolant
shape,
res,
octaves=1,
persistence=0.5,
lacunarity=2,
tileable=(False, False, False),
interpolant=interpolant,
seed=None,
):
"""Generate a 3D numpy array of fractal noise.

Expand Down Expand Up @@ -104,13 +157,18 @@ def generate_fractal_noise_3d(
noise = np.zeros(shape)
frequency = 1
amplitude = 1

for _ in range(octaves):
noise += amplitude * generate_perlin_noise_3d(
octave_noise = generate_perlin_noise_3d(
shape,
(frequency*res[0], frequency*res[1], frequency*res[2]),
(frequency * res[0], frequency * res[1], frequency * res[2]),
tileable,
interpolant
interpolant,
seed,
)

noise += amplitude * octave_noise
frequency *= lacunarity
amplitude *= persistence

return noise