163 lines
4.4 KiB
Python
163 lines
4.4 KiB
Python
"""
|
|
Perlin Noise 1
|
|
|
|
If Python and Arcade are installed, this example can be run from the command line with:
|
|
python -m arcade.examples.perlin_noise_1
|
|
|
|
TODO: This code doesn't work properly, and isn't currently listed in the examples.
|
|
"""
|
|
import arcade
|
|
import numpy as np
|
|
from PIL import Image
|
|
|
|
# Set how many rows and columns we will have
|
|
ROW_COUNT = 30
|
|
COLUMN_COUNT = 30
|
|
|
|
# This sets the WIDTH and HEIGHT of each grid location
|
|
WIDTH = 10
|
|
HEIGHT = 10
|
|
|
|
# This sets the margin between each cell
|
|
# and on the edges of the screen.
|
|
MARGIN = 2
|
|
|
|
# Do the math to figure out our screen dimensions
|
|
SCREEN_WIDTH = (WIDTH + MARGIN) * COLUMN_COUNT + MARGIN
|
|
SCREEN_HEIGHT = (HEIGHT + MARGIN) * ROW_COUNT + MARGIN
|
|
SCREEN_TITLE = "Perlin Noise 1 Example"
|
|
|
|
|
|
# Perlin noise generator from:
|
|
# https://stackoverflow.com/questions/42147776/producing-2d-perlin-noise-with-numpy
|
|
|
|
def perlin(x, y, seed=0):
|
|
# permutation table
|
|
np.random.seed(seed)
|
|
p = np.arange(256, dtype=int)
|
|
np.random.shuffle(p)
|
|
p = np.stack([p, p]).flatten()
|
|
# coordinates of the top-left
|
|
xi = x.astype(int)
|
|
yi = y.astype(int)
|
|
# internal coordinates
|
|
xf = x - xi
|
|
yf = y - yi
|
|
# fade factors
|
|
u = fade(xf)
|
|
v = fade(yf)
|
|
# noise components
|
|
n00 = gradient(p[p[xi] + yi], xf, yf)
|
|
n01 = gradient(p[p[xi] + yi + 1], xf, yf - 1)
|
|
n11 = gradient(p[p[xi + 1] + yi + 1], xf - 1, yf - 1)
|
|
n10 = gradient(p[p[xi + 1] + yi], xf - 1, yf)
|
|
# combine noises
|
|
x1 = lerp(n00, n10, u)
|
|
x2 = lerp(n01, n11, u) # FIX1: I was using n10 instead of n01
|
|
return lerp(x1, x2, v) # FIX2: I also had to reverse x1 and x2 here
|
|
|
|
|
|
def lerp(a, b, x):
|
|
"""linear interpolation"""
|
|
return a + x * (b - a)
|
|
|
|
|
|
def fade(t):
|
|
"""6t^5 - 15t^4 + 10t^3"""
|
|
return 6 * t ** 5 - 15 * t ** 4 + 10 * t ** 3
|
|
|
|
|
|
def gradient(h, x, y):
|
|
"""grad converts h to the right gradient vector and return the dot product with (x,y)"""
|
|
vectors = np.array([[0, 1], [0, -1], [1, 0], [-1, 0]])
|
|
g = vectors[h % 4]
|
|
return g[:, :, 0] * x + g[:, :, 1] * y
|
|
|
|
|
|
class MyGame(arcade.Window):
|
|
"""
|
|
Main application class.
|
|
"""
|
|
|
|
def __init__(self, width, height, title):
|
|
"""
|
|
Set up the application.
|
|
"""
|
|
super().__init__(width, height, title)
|
|
|
|
self.shape_list = None
|
|
|
|
arcade.set_background_color(arcade.color.BLACK)
|
|
|
|
self.grid = None
|
|
self.recreate_grid()
|
|
|
|
def recreate_grid(self):
|
|
lin = np.linspace(0, 5, ROW_COUNT, endpoint=False)
|
|
y, x = np.meshgrid(lin, lin)
|
|
self.grid = (perlin(x, y, seed=0))
|
|
|
|
self.grid *= 255
|
|
self.grid += 128
|
|
|
|
# for row in range(ROW_COUNT):
|
|
# for column in range(COLUMN_COUNT):
|
|
# print(f"{self.grid[row][column]:5.2f} ", end="")
|
|
# print()
|
|
|
|
self.shape_list = arcade.ShapeElementList()
|
|
for row in range(ROW_COUNT):
|
|
for column in range(COLUMN_COUNT):
|
|
color = self.grid[row][column], 0, 0
|
|
|
|
x = (MARGIN + WIDTH) * column + MARGIN + WIDTH // 2
|
|
y = (MARGIN + HEIGHT) * row + MARGIN + HEIGHT // 2
|
|
|
|
current_rect = arcade.create_rectangle_filled(x, y, WIDTH, HEIGHT, color)
|
|
self.shape_list.append(current_rect)
|
|
|
|
im = Image.fromarray(np.uint8(self.grid), "L")
|
|
im.save("test.png")
|
|
|
|
def on_draw(self):
|
|
"""
|
|
Render the screen.
|
|
"""
|
|
|
|
# This command has to happen before we start drawing
|
|
arcade.start_render()
|
|
|
|
self.shape_list.draw()
|
|
|
|
def on_mouse_press(self, x, y, button, modifiers):
|
|
"""
|
|
Called when the user presses a mouse button.
|
|
"""
|
|
|
|
# Change the x/y screen coordinates to grid coordinates
|
|
column = x // (WIDTH + MARGIN)
|
|
row = y // (HEIGHT + MARGIN)
|
|
|
|
print(f"Click coordinates: ({x}, {y}). Grid coordinates: ({row}, {column})")
|
|
|
|
# Make sure we are on-grid. It is possible to click in the upper right
|
|
# corner in the margin and go to a grid location that doesn't exist
|
|
if row < ROW_COUNT and column < COLUMN_COUNT:
|
|
|
|
# Flip the location between 1 and 0.
|
|
if self.grid[row][column] == 0:
|
|
self.grid[row][column] = 1
|
|
else:
|
|
self.grid[row][column] = 0
|
|
|
|
self.recreate_grid()
|
|
|
|
|
|
def main():
|
|
MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
|
|
arcade.run()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|