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

numpy-replacements for image-drawing #161

Merged
merged 4 commits into from
Nov 2, 2015
Merged
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
149 changes: 59 additions & 90 deletions worldengine/draw.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import numpy.ma as ma

from worldengine.drawing_functions import draw_ancientmap, \
draw_rivers_on_image, gradient
draw_rivers_on_image

# -------------
# Helper values
Expand Down Expand Up @@ -168,43 +168,31 @@ def draw_simple_elevation(world, sea_level, target):
""" This function can be used on a generic canvas (either an image to save
on disk or a canvas part of a GUI)
"""
min_elev_sea = None
max_elev_sea = None
min_elev_land = None
max_elev_land = None
for y in range(world.height):
for x in range(world.width):
e = world.elevation['data'][y,x]
if sea_level is None:
if min_elev_land is None or e < min_elev_land:
min_elev_land = e
if max_elev_land is None or e > max_elev_land:
max_elev_land = e
elif world.is_land((x, y)):
if min_elev_land is None or e < min_elev_land:
min_elev_land = e
if max_elev_land is None or e > max_elev_land:
max_elev_land = e
else:
if min_elev_sea is None or e < min_elev_sea:
min_elev_sea = e
if max_elev_sea is None or e > max_elev_sea:
max_elev_sea = e
e = world.elevation['data']
c = numpy.empty(e.shape, dtype=numpy.float)

has_ocean = not (sea_level is None or world.ocean is None or not world.ocean.any()) # or 'not any ocean'
mask_land = numpy.ma.array(e, mask=world.ocean if has_ocean else False) # only land

elev_delta_land = (max_elev_land - min_elev_land)/11
if sea_level != None:
min_elev_land = mask_land.min()
max_elev_land = mask_land.max()
elev_delta_land = (max_elev_land - min_elev_land) / 11.0

if has_ocean:
land = numpy.logical_not(world.ocean)
mask_ocean = numpy.ma.array(e, mask=land) # only ocean
min_elev_sea = mask_ocean.min()
max_elev_sea = mask_ocean.max()
elev_delta_sea = max_elev_sea - min_elev_sea


c[world.ocean] = ((e[world.ocean] - min_elev_sea) / elev_delta_sea)
c[land] = ((e[land] - min_elev_land) / elev_delta_land) + 1
else:
c = ((e - min_elev_land) / elev_delta_land) + 1

for y in range(world.height):
for x in range(world.width):
e = world.elevation['data'][y, x]
if sea_level is None:
c = ((e - min_elev_land) / elev_delta_land) + 1
elif world.is_land((x, y)):
c = ((e - min_elev_land) / elev_delta_land) + 1
else:
c = ((e - min_elev_sea) / elev_delta_sea)
r, g, b = elevation_color(c, sea_level)
r, g, b = elevation_color(c[y, x], sea_level)
target.set_pixel(x, y, (int(r * 255), int(g * 255),
int(b * 255), 255))

Expand All @@ -213,46 +201,34 @@ def draw_riversmap(world, target):
sea_color = (255, 255, 255, 255)
land_color = (0, 0, 0, 255)

for y in range(world.height):#TODO: numpy
for y in range(world.height):
for x in range(world.width):
if world.ocean[y, x]:
target.set_pixel(x, y, sea_color)
else:
target.set_pixel(x, y, land_color)
target.set_pixel(x, y, sea_color if world.is_ocean((x, y)) else land_color)

draw_rivers_on_image(world, target, factor=1)


def draw_grayscale_heightmap(world, target):
min_elev_sea = None
max_elev_sea = None
min_elev_land = None
max_elev_land = None
for y in range(world.height):
for x in range(world.width):
e = world.elevation['data'][y, x]
if world.is_land((x, y)):
if min_elev_land is None or e < min_elev_land:
min_elev_land = e
if max_elev_land is None or e > max_elev_land:
max_elev_land = e
else:
if min_elev_sea is None or e < min_elev_sea:
min_elev_sea = e
if max_elev_sea is None or e > max_elev_sea:
max_elev_sea = e
e = world.elevation['data']

mask = numpy.ma.array(e, mask=world.ocean) # only land
min_elev_land = mask.min()
max_elev_land = mask.max()
elev_delta_land = max_elev_land - min_elev_land

mask = numpy.ma.array(e, mask=numpy.logical_not(world.ocean)) # only ocean
min_elev_sea = mask.min()
max_elev_sea = mask.max()
elev_delta_sea = max_elev_sea - min_elev_sea

c = numpy.empty(e.shape, dtype=numpy.float)
c[numpy.invert(world.ocean)] = (e[numpy.invert(world.ocean)] - min_elev_land) * 127 / elev_delta_land + 128
c[world.ocean] = (e[world.ocean] - min_elev_sea) * 127 / elev_delta_sea
c = numpy.rint(c).astype(dtype=numpy.int32) # proper rounding
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was not rounded before, which might even be preferable.


for y in range(world.height):
for x in range(world.width):
e = world.elevation['data'][y, x]
if world.is_land((x, y)):
c = int(((e - min_elev_land) * 127) / elev_delta_land)+128
else:
c = int(((e - min_elev_sea) * 127) / elev_delta_sea)
target.set_pixel(x, y, (c, c, c, 255))
target.set_pixel(x, y, (c[y, x], c[y, x], c[y, x], 255))


def draw_elevation(world, shadow, target):
Expand All @@ -262,13 +238,13 @@ def draw_elevation(world, shadow, target):
data = world.elevation['data']
ocean = world.ocean

mask = numpy.ma.array(data, mask = ocean)
mask = numpy.ma.array(data, mask=ocean)

min_elev = mask.min()
max_elev = mask.max()
elev_delta = max_elev - min_elev

for y in range(height):#TODO: numpy optimisation for the code below
for y in range(height):
for x in range(width):
if ocean[y, x]:
target.set_pixel(x, y, (0, 0, 255, 255))
Expand Down Expand Up @@ -307,24 +283,16 @@ def draw_precipitation(world, target, black_and_white=False):
height = world.height

if black_and_white:
low = None
high = None
for y in range(height):
for x in range(width):
p = world.precipitations_at((x, y))
if low is None or p < low:
low = p
if high is None or p > high:
high = p
low = world.precipitation['data'].min()
high = world.precipitation['data'].max()
floor = 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for the variables with clear names

ceiling = 255

colors = numpy.interp(world.precipitation['data'], [low, high], [floor, ceiling])
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am making use of numpys interp-method (which basically does what drawing_functions.py/gradient() did before). I assume it is faster.
I also prepared things for the 16 Bit grayscale-output that can also be used for temperature and precipitation, if wanted.

colors = numpy.rint(colors).astype(dtype=numpy.int32) # proper rounding
for y in range(height):
for x in range(width):
p = world.precipitations_at((x, y))
if p <= low:
target.set_pixel(x, y, (0, 0, 0, 255))
elif p >= high:
target.set_pixel(x, y, (255, 255, 255, 255))
else:
target.set_pixel(x, y, gradient(p, low, high, (0, 0, 0), (255, 255, 255)))
target.set_pixel(x, y, (colors[y, x], colors[y, x], colors[y, x], 255))
else:
for y in range(height):
for x in range(width):
Expand Down Expand Up @@ -367,15 +335,14 @@ def draw_temperature_levels(world, target, black_and_white=False):
if black_and_white:
low = world.temperature_thresholds()[0][1]
high = world.temperature_thresholds()[5][1]
floor = 0
ceiling = 255

colors = numpy.interp(world.temperature['data'], [low, high], [floor, ceiling])
colors = numpy.rint(colors).astype(dtype=numpy.int32) # proper rounding
for y in range(height):
for x in range(width):
t = world.temperature_at((x, y))
if t <= low:
target.set_pixel(x, y, (0, 0, 0, 255))
elif t >= high:
target.set_pixel(x, y, (255, 255, 255, 255))
else:
target.set_pixel(x, y, gradient(t, low, high, (0, 0, 0), (255, 255, 255)))
target.set_pixel(x, y, (colors[y, x], colors[y, x], colors[y, x], 255))

else:
for y in range(height):
Expand Down Expand Up @@ -407,6 +374,7 @@ def draw_biome(world, target):
v = biome[y, x]
target.set_pixel(x, y, _biome_colors[v])


def draw_scatter_plot(world, size, target):
""" This function can be used on a generic canvas (either an image to save
on disk or a canvas part of a GUI)
Expand All @@ -431,7 +399,7 @@ def draw_scatter_plot(world, size, target):
#fill in 'bad' boxes with grey
h_values = ['62', '50', '37', '25', '12']
t_values = [ 0, 1, 2, 3, 5 ]
for loop in range(0,5):
for loop in range(0, 5):
h_min = (size - 1) * ((world.humidity['quantiles'][h_values[loop]] - min_humidity) / humidity_delta)
if loop != 4:
h_max = (size - 1) * ((world.humidity['quantiles'][h_values[loop + 1]] - min_humidity) / humidity_delta)
Expand All @@ -455,13 +423,13 @@ def draw_scatter_plot(world, size, target):
#draw lines based on thresholds
for t in range(0, 6):
v = (size - 1) * ((world.temperature['thresholds'][t][1] - min_temperature) / temperature_delta)
if v > 0 and v < size:
if 0 < v < size:
for y in range(0, size):
target.set_pixel(int(v), (size - 1) - y, (0, 0, 0, 255))
ranges = ['87', '75', '62', '50', '37', '25', '12']
for p in ranges:
h = (size - 1) * ((world.humidity['quantiles'][p] - min_humidity) / humidity_delta)
if h > 0 and h < size:
if 0 < h < size:
for x in range(0, size):
target.set_pixel(x, (size - 1) - int(h), (0, 0, 0, 255))

Expand Down Expand Up @@ -583,6 +551,7 @@ def draw_ancientmap_on_file(world, filename, resize_factor=1,
verbose)
img.complete()


def draw_scatter_plot_on_file(world, filename):
img = ImagePixelSetter(512, 512, filename)
draw_scatter_plot(world, 512, img)
Expand Down
Loading