124 lines
3.2 KiB
Python
Executable File
124 lines
3.2 KiB
Python
Executable File
#! /usr/bin/env python3
|
|
|
|
from PIL import Image
|
|
import random
|
|
import math
|
|
|
|
WIDTH = 128
|
|
HEIGHT = 128
|
|
|
|
BIOMES = 16
|
|
PERLIN_CELL_SIZE = 32
|
|
|
|
# Voronoi diagram by jump flooding
|
|
def voronoi(seeds):
|
|
world = [[-1] * WIDTH for i in range(HEIGHT)]
|
|
for i, (x, y) in enumerate(seeds):
|
|
world[y][x] = i
|
|
|
|
N = WIDTH // 2
|
|
while N > 0:
|
|
for py in range(HEIGHT):
|
|
for px in range(WIDTH):
|
|
for dy in [-N, 0, N]:
|
|
for dx in [-N, 0, N]:
|
|
qx, qy = px + dx, py + dy
|
|
if qx < 0 or qx >= WIDTH or qy < 0 or qy >= HEIGHT:
|
|
continue
|
|
|
|
if world[py][px] < 0 and world[qy][qx] >= 0:
|
|
world[py][px] = world[qy][qx]
|
|
elif world[py][px] >= 0 and world[qy][qx] >= 0:
|
|
spx, spy = seeds[world[py][px]]
|
|
sqx, sqy = seeds[world[qy][qx]]
|
|
dist_sp_2 = (py - spy) ** 2 + (px - spx) ** 2
|
|
dist_sq_2 = (py - sqy) ** 2 + (px - sqx) ** 2
|
|
if dist_sq_2 < dist_sp_2:
|
|
world[py][px] = world[qy][qx]
|
|
N //= 2
|
|
|
|
return world
|
|
|
|
# --== Generate a biome center map with the jump-flood algorithm ==--
|
|
|
|
def random_color():
|
|
r = random.randint(0, 256)
|
|
g = random.randint(0, 256)
|
|
b = random.randint(0, 256)
|
|
return (r, g, b)
|
|
|
|
def random_palette(N):
|
|
return [random_color() for i in range(N)]
|
|
|
|
seeds = [(random.randint(0, WIDTH-1), random.randint(0, HEIGHT-1))
|
|
for i in range(BIOMES)]
|
|
biomes = voronoi(seeds)
|
|
|
|
def show_biomes(biomes):
|
|
img = Image.new("RGBA", (WIDTH, HEIGHT))
|
|
px = img.load()
|
|
palette = random_palette(BIOMES)
|
|
|
|
for y in range(HEIGHT):
|
|
for x in range(WIDTH):
|
|
px[x,y] = palette[biomes[y][x]]
|
|
|
|
img.show()
|
|
|
|
# show_biomes(biomes)
|
|
|
|
# --== Generate some Perlin noise ==--
|
|
|
|
pregradients = []
|
|
for i in range(16):
|
|
alpha = 2 * math.pi * i / 16
|
|
pregradients.append((math.cos(alpha), math.sin(alpha)))
|
|
|
|
def random_grid(N):
|
|
N = WIDTH // PERLIN_CELL_SIZE + 1
|
|
return [[random.choice(pregradients) for x in range(N)] for y in range(N)]
|
|
|
|
def smooth(t):
|
|
return t * t * t * (t * (6 * t - 15) + 10)
|
|
|
|
def interp(x, y, t):
|
|
return y * t + x * (1.0 - t)
|
|
|
|
def dot(grid, ix, iy, fx, fy):
|
|
gx, gy = grid[iy][ix]
|
|
return (fx - ix) * gx + (fy - iy) * gy
|
|
|
|
def perlin_at(grid, fx, fy):
|
|
ix = int(fx)
|
|
iy = int(fy)
|
|
|
|
d0 = dot(grid, ix, iy, fx, fy)
|
|
d1 = dot(grid, ix+1, iy, fx, fy)
|
|
|
|
d2 = dot(grid, ix, iy+1, fx, fy)
|
|
d3 = dot(grid, ix+1, iy+1, fx, fy)
|
|
|
|
rx = smooth(fx - ix)
|
|
ry = smooth(fy - iy)
|
|
|
|
return interp(interp(d0, d1, rx), interp(d2, d3, rx), ry)
|
|
|
|
PERLIN_N = WIDTH // PERLIN_CELL_SIZE
|
|
grid = random_grid(PERLIN_N)
|
|
|
|
def show_perlin(grid):
|
|
img = Image.new("RGBA", (WIDTH, HEIGHT))
|
|
px = img.load()
|
|
|
|
for y in range(HEIGHT):
|
|
for x in range(WIDTH):
|
|
fx, fy = x / PERLIN_CELL_SIZE, y / PERLIN_CELL_SIZE
|
|
noise = perlin_at(grid, fx, fy)
|
|
|
|
gray = 128 + round(noise * 128)
|
|
px[x, y] = (gray, gray, gray)
|
|
|
|
img.show()
|
|
|
|
show_perlin(grid)
|