img = np.random.ranf((128,128))
plt.imshow(img, cmap=plt.cm.ocean)

"""
Explanation: plotting images
We can easily plot images by using the imshow function. Conveniently, an image can just be a 2-dimensional numpy array of floats in the range of 0 to 1. We can easily create such an array with the ranf function. Below we create a 128x128 image of random values and display that using the ocean colormap from the pyplot.cm module.
End of explanation
"""


def seed(n, shape=(8,)):
    global r
    np.random.seed(n)
    r = np.random.ranf(shape)

"""
Explanation: value noise
The image above is an example of random noise. Although useful we need something a bit more controllable. For this we need a function that produces value noise instead of just random noise. Essentially this is just a function that given a vector of inputs, produces an float in the range of 0 to 1. For now we'll focus on one-dimensional noise but we'll deal with noise in more dimensions later.
There's various ways to produce value noise but we're gonna use a method that utilizes a seeded vector of random values. For no particular reason we're going to call this vector r and we'll use a numpy array to hold it. For convenience we will also write a function seed that we can use to initialize the table.
End of explanation
"""


seed(0)
x = np.arange(0, len(r), 1)
plt.plot(x, r[x], 'bo')
plt.axis('tight')

"""
Explanation: We can visualize r by plotting it.
End of explanation
"""


def noise(x):
    x = int(x % len(r))
    return r[x]

"""
Explanation: We can use this table to define a function noise that given an integer value x will return a value in the range of 0 to 1. The r vector only has a limited amount of values. In order to make it work for any value of x we can perform a modulo operation with the length of the r vector.
End of explanation
"""


x = np.arange(0, 16, 1)
y = [noise(x) for x in x]
plt.plot(x, y, 'bo')

"""
Explanation: By default we only have 8 values in the r vector so if we plot the noise function over a range 0 to 16 we can see that it repeats itself halfway.
End of explanation
"""


def noise(x):
    xi = int(x)
    x0 = xi % len(r)
    x1 = 0 if x0 == (len(r) - 1) else (x0 + 1)
    v0, v1 = r[x0], r[x1]
    t = x - xi
    return np.interp(t, [0, 1], [v0, v1])

x = np.arange(0, 16, 1/10)
y = [noise(x) for x in x]
plt.plot(x, y)

"""
Explanation: Our noise function works for integer values of x ideally it works for real values of x as well. We can do this by interpolation.
End of explanation
"""


seed(0, (4,4))

"""
Explanation: Now let's create some noise in two-dimensions. We'll implement a naive solution at first and revisit it later. Let's start by re-seeding the r table in two dimensions.
End of explanation
"""


def noise(x, y):
    xi, yi = int(x), int(y)
    tx, ty = x - xi, y - yi
    x0 = xi % len(r)
    x1 = 0 if x0 == (len(r) - 1) else x0 + 1
    y0 = yi % len(r[0])
    y1 = 0 if y0 == (len(r) - 1) else y0 + 1
    c00 = r[x0][y0]
    c01 = r[x1][y0]
    c10 = r[x0][y1]
    c11 = r[x1][y1]
    n0 = np.interp(tx, [0, 1], [c00, c01])
    n1 = np.interp(tx, [0, 1], [c10, c11])
    return np.interp(ty, [0, 1], [n0, n1])

img = np.array([[noise(x, y) for x in range(16)] for y in range(16)])
plt.imshow(img, plt.cm.ocean)

"""
Explanation: And now we redefine our noise function so it will interpolate between the x and y value as well.
End of explanation
"""


seed(0, (16,16))
img = np.array([[noise(x, y) for x in range(32)] for y in range(32)])
plt.imshow(img, cmap=plt.cm.ocean)

"""
Explanation: We can see clearly see a pattern here. That's because the dimensions of our r table are very small, only 4 in each axis. We'll get much better noise if we make it bigger.
End of explanation
"""


def smoothstep(t):
    return 3 * t**2 - 2 * t**3

x = np.linspace(0, 1, 1000)
y = smoothstep(x)
plt.plot(x, y)

"""
Explanation: There's still a pattern but that is to be expected with our still reasonably small r vector. It does like a lot better already but another thing that is noticeable is that it appears to be a bit blocky. This is because of the linear interpolation we're using to calculate the final noise values. We can fix this by introducing the smoothstep function.
smoothstep
End of explanation
"""


def noise(x, y, c = lambda t: t):
    xi, yi = int(x), int(y)
    tx, ty = x - xi, y - yi
    x0 = xi % len(r)
    x1 = 0 if x0 == (len(r) - 1) else x0 + 1
    y0 = yi % len(r[0])
    y1 = 0 if y0 == (len(r) - 1) else y0 + 1
    c00 = r[x0][y0]
    c01 = r[x1][y0]
    c10 = r[x0][y1]
    c11 = r[x1][y1]
    sx = c(tx)
    sy = c(ty)
    n0 = np.interp(sx, [0, 1], [c00, c01])
    n1 = np.interp(sx, [0, 1], [c10, c11])
    return np.interp(sy, [0, 1], [n0, n1])

"""
Explanation: And now we have to adjust our noise function to incorporate the smoothstep function.
End of explanation
"""


def gen_im(size=(64,64), c = lambda x: x):
    w, h = size
    pixels = [[noise(x, y, c) for x in range(w)] for y in range(h)]
    return np.array(pixels)

seed(1, (32, 32))
img = gen_im(c = smoothstep)
plt.imshow(img, cmap=plt.cm.ocean)

MAX_VERTICES = 32
MAX_VERTICES_MASK = MAX_VERTICES - 1


"""
Explanation: Note that instead of interpolating on tx and ty directly we are using the smoothstep-ed values of sx and sy instead.
End of explanation
"""


