Source code for bravo.simplex

from __future__ import division

import math
from itertools import chain, izip, permutations
from random import Random

SIZE = 2**10

edges2 = list(
    set(
        chain(
            permutations((0, 1, 1), 3),
            permutations((0, 1, -1), 3),
            permutations((0, -1, -1), 3),
        )
    )
)
edges2.sort()

edges3 = list(
    set(
        chain(
            permutations((0, 1, 1, 1), 4),
            permutations((0, 1, 1, -1), 4),
            permutations((0, 1, -1, -1), 4),
            permutations((0, -1, -1, -1), 4),
        )
    )
)
edges3.sort()

[docs]def dot2(u, v): """ Dot product of two 2-dimensional vectors. """ return u[0] * v[0] + u[1] * v[1]
[docs]def dot3(u, v): """ Dot product of two 3-dimensional vectors. """ return u[0] * v[0] + u[1] * v[1] + u[2] * v[2]
[docs]def reseed(seed): """ Reseed the simplex gradient field. """ if seed in fields: return p = range(SIZE) r = Random() r.seed(seed) r.shuffle(p) p *= 2 fields[seed] = p
[docs]def set_seed(seed): """ Set the current seed. """ global current_seed reseed(seed) current_seed = seed
fields = dict() current_seed = None f2 = 0.5 * (math.sqrt(3) - 1) g2 = (3 - math.sqrt(3)) / 6
[docs]def simplex2(x, y): """ Generate simplex noise at the given coordinates. This particular implementation has very high chaotic features at normal resolution; zooming in by a factor of 16x to 256x is going to yield more pleasing results for most applications. The gradient field must be seeded prior to calling this function; call ``reseed()`` first. :param int x: X coordinate :param int y: Y coordinate :returns: simplex noise :raises Exception: the gradient field is not seeded """ if current_seed is None: raise Exception("The gradient field is unseeded!") p = fields[current_seed] # Set up our scalers and arrays. coords = [None] * 3 gradients = [None] * 3 s = (x + y) * f2 i = math.floor(x + s) j = math.floor(y + s) t = (i + j) * g2 x -= i - t y -= j - t # Clamp to the size of the simplex array. i = int(i) % SIZE j = int(j) % SIZE # Look up coordinates and gradients for each contributing point in the # simplex space. coords[0] = x, y gradients[0] = p[i + p[j]] if x > y: coords[1] = x - 1 + g2, y + g2 gradients[1] = p[i + 1 + p[j ]] else: coords[1] = x + g2, y - 1 + g2 gradients[1] = p[i + p[j + 1]] coords[2] = x - 1 + 2 * g2, y - 1 + 2 * g2 gradients[2] = p[i + 1 + p[j + 1]] # Do our summation. n = 0 for coord, gradient in izip(coords, gradients): t = 0.5 - coord[0] * coord[0] - coord[1] * coord[1] if t > 0: n += t**4 * dot2(edges2[gradient % 12], coord) # Where's this scaling factor come from? return n * 70
[docs]def simplex3(x, y, z): """ Generate simplex noise at the given coordinates. This is a 3-dimensional flavor of ``simplex2()``; all of the same caveats apply. The gradient field must be seeded prior to calling this function; call ``reseed()`` first. :param int x: X coordinate :param int y: Y coordinate :param int z: Z coordinate :returns: simplex noise :raises Exception: the gradient field is not seeded or you broke the function somehow """ if current_seed is None: raise Exception("The gradient field is unseeded!") p = fields[current_seed] f = 1 / 3 g = 1 / 6 coords = [None] * 4 gradients = [None] * 4 s = (x + y + z) * f i = math.floor(x + s) j = math.floor(y + s) k = math.floor(z + s) t = (i + j + k) * g x -= i - t y -= j - t z -= k - t i = int(i) % SIZE j = int(j) % SIZE k = int(k) % SIZE # Do the coord and gradient lookups. Unrolled for speed and clarity. # These should be + 2 * g, but instead we do + f because we already have # it calculated. (2g == 2/6 == 1/3 == f) coords[0] = x, y, z gradients[0] = p[i + p[j + p[k]]] if x >= y >= z: coords[1] = x - 1 + g, y + g, z + g coords[2] = x - 1 + f, y - 1 + f, z + f gradients[1] = p[i + 1 + p[j + p[k ]]] gradients[2] = p[i + 1 + p[j + 1 + p[k ]]] elif x >= z >= y: coords[1] = x - 1 + g, y + g, z + g coords[2] = x - 1 + f, y + f, z - 1 + f gradients[1] = p[i + 1 + p[j + p[k ]]] gradients[2] = p[i + 1 + p[j + p[k + 1]]] elif z >= x >= y: coords[1] = x + g, y + g, z - 1 + g coords[2] = x - 1 + f, y + f, z - 1 + f gradients[1] = p[i + p[j + p[k + 1]]] gradients[2] = p[i + 1 + p[j + p[k + 1]]] elif z >= y >= x: coords[1] = x + g, y + g, z - 1 + g coords[2] = x + f, y - 1 + f, z - 1 + f gradients[1] = p[i + p[j + p[k + 1]]] gradients[2] = p[i + p[j + 1 + p[k + 1]]] elif y >= z >= x: coords[1] = x + g, y - 1 + g, z + g coords[2] = x + f, y - 1 + f, z - 1 + f gradients[1] = p[i + p[j + 1 + p[k ]]] gradients[2] = p[i + p[j + 1 + p[k + 1]]] elif y >= x >= z: coords[1] = x + g, y - 1 + g, z + g coords[2] = x - 1 + f, y - 1 + f, z + f gradients[1] = p[i + p[j + 1 + p[k ]]] gradients[2] = p[i + 1 + p[j + 1 + p[k ]]] else: raise Exception("You broke maths. Good work.") coords[3] = x - 1 + 0.5, y - 1 + 0.5, z - 1 + 0.5 gradients[3] = p[i + 1 + p[j + 1 + p[k + 1]]] n = 0 for coord, gradient in izip(coords, gradients): t = (0.6 - coord[0] * coord[0] - coord[1] * coord[1] - coord[2] * coord[2]) if t > 0: n += t**4 * dot3(edges2[gradient % 12], coord) # Where's this scaling factor come from? return n * 32
def simplex(*args): if len(args) == 2: return simplex2(*args) if len(args) == 3: return simplex3(*args) else: raise Exception("Don't know how to do %dD noise!" % len(args))
[docs]def octaves2(x, y, count): """ Generate fractal octaves of noise. Summing increasingly scaled amounts of noise with itself creates fractal clouds of noise. :param int x: X coordinate :param int y: Y coordinate :param int count: number of octaves :returns: Scaled fractal noise """ sigma = 0 divisor = 1 while count: sigma += simplex2(x * divisor, y * divisor) / divisor divisor *= 2 count -= 1 return sigma
[docs]def octaves3(x, y, z, count): """ Generate fractal octaves of noise. :param int x: X coordinate :param int y: Y coordinate :param int z: Z coordinate :param int count: number of octaves :returns: Scaled fractal noise """ sigma = 0 divisor = 1 while count: sigma += simplex3(x * divisor, y * divisor, z * divisor) / divisor divisor *= 2 count -= 1 return sigma
[docs]def offset2(x, y, xoffset, yoffset, octaves=1): """ Generate an offset noise difference field. :param int x: X coordinate :param int y: Y coordinate :param int xoffset: X offset :param int yoffset: Y offset :returns: Difference of noises """ return (octaves2(x, y, octaves) - octaves2(x + xoffset, y + yoffset, octaves) + 1) * 0.5

Project Versions

This Page