quadtree generation sorta works
This commit is contained in:
154
world.py
154
world.py
@@ -31,6 +31,14 @@ def base36encode(number):
|
|||||||
else:
|
else:
|
||||||
return base36
|
return base36
|
||||||
|
|
||||||
|
def load_sort_and_process(worlddir):
|
||||||
|
"""Takes a directory to a world dir, and returns a mapping from (col, row)
|
||||||
|
to result object"""
|
||||||
|
all_chunks = find_chunkfiles(worlddir)
|
||||||
|
mincol, maxcol, minrow, maxrow, translated_chunks = convert_coords(all_chunks)
|
||||||
|
results = render_chunks_async(translated_chunks, caves=False, processes=5)
|
||||||
|
return results
|
||||||
|
|
||||||
def find_chunkfiles(worlddir):
|
def find_chunkfiles(worlddir):
|
||||||
"""Returns a list of all the chunk file locations, and the file they
|
"""Returns a list of all the chunk file locations, and the file they
|
||||||
correspond to.
|
correspond to.
|
||||||
@@ -208,11 +216,27 @@ def render_worldtile(chunkmap, colstart, colend, rowstart, rowend):
|
|||||||
|
|
||||||
The image object is returned.
|
The image object is returned.
|
||||||
"""
|
"""
|
||||||
# width of one chunk is 384. Each column is half a chunk wide.
|
# width of one chunk is 384. Each column is half a chunk wide. The total
|
||||||
|
# width is (384 + 192*(numcols-1)) since the first column contributes full
|
||||||
|
# width, and each additional one contributes half since they're staggered.
|
||||||
|
# However, since we want to cut off half a chunk at each end (384 less
|
||||||
|
# pixels) and since (colend - colstart + 1) is the number of columns
|
||||||
|
# inclusive, the equation simplifies to:
|
||||||
width = 192 * (colend - colstart)
|
width = 192 * (colend - colstart)
|
||||||
# Same deal with height
|
# Same deal with height
|
||||||
height = 96 * (rowend - rowstart)
|
height = 96 * (rowend - rowstart)
|
||||||
# I know those equations could be simplified. Left like that for clarity
|
|
||||||
|
# The standard tile size is 3 columns by 5 rows, which works out to 384x384
|
||||||
|
# pixels for 8 total chunks. (Since the chunks are staggered but the grid
|
||||||
|
# is not, some grid coordinates do not address chunks) The two chunks on
|
||||||
|
# the middle column are shown in full, the two chunks in the middle row are
|
||||||
|
# half cut off, and the four remaining chunks are one quarter shown.
|
||||||
|
# The above example with cols 0-3 and rows 0-4 has the chunks arranged like this:
|
||||||
|
# 0,0 2,0
|
||||||
|
# 1,1
|
||||||
|
# 0,2 2,2
|
||||||
|
# 1,3
|
||||||
|
# 0,4 2,4
|
||||||
|
|
||||||
tileimg = Image.new("RGBA", (width, height))
|
tileimg = Image.new("RGBA", (width, height))
|
||||||
|
|
||||||
@@ -240,12 +264,12 @@ def render_worldtile(chunkmap, colstart, colend, rowstart, rowend):
|
|||||||
|
|
||||||
return tileimg
|
return tileimg
|
||||||
|
|
||||||
def generate_quadtree(chunkmap, colstart, colend, rowstart, rowend, prefix):
|
def generate_quadtree(chunkmap, colstart, colend, rowstart, rowend, prefix, quadrent="base"):
|
||||||
"""Recursive method that generates a quadtree.
|
"""Recursive method that generates a quadtree.
|
||||||
A single call generates, saves, and returns an image with the range
|
A single call generates, saves, and returns an image with the range
|
||||||
specified by colstart,colend,rowstart, and rowend.
|
specified by colstart,colend,rowstart, and rowend.
|
||||||
|
|
||||||
The image is saved as prefix+".png"
|
The image is saved as os.path.join(prefix, quadrent+".png")
|
||||||
|
|
||||||
If the requested range is larger than a certain threshold, this method will
|
If the requested range is larger than a certain threshold, this method will
|
||||||
instead make 4 calls to itself to render the 4 quadrents of the image. The
|
instead make 4 calls to itself to render the 4 quadrents of the image. The
|
||||||
@@ -255,20 +279,114 @@ def generate_quadtree(chunkmap, colstart, colend, rowstart, rowend, prefix):
|
|||||||
If the requested range is not too large, it is generated with
|
If the requested range is not too large, it is generated with
|
||||||
render_worldtile()
|
render_worldtile()
|
||||||
|
|
||||||
If the path "prefix" exists and is a directory, this call is assumed to be
|
The path "prefix" should be a directory where this call should save its
|
||||||
the "initial" recursive call, and will save the image as "base.png" in that
|
image.
|
||||||
directory. Recursed calls will have prefix set to os.path.join(prefix, "#")
|
|
||||||
where # is 0, 1, 2, or 3.
|
|
||||||
|
|
||||||
The last piece to the puzzle is how directories are created. If a call
|
quadrent is used in recursion. If it is "base", the image is saved in the
|
||||||
wants to save an image as tiles/0/0.png and directory tiles/0 doesn't
|
directory named by prefix, and recursive calls will have quadrent set to
|
||||||
exist, it will be created.
|
"0" "1" "2" or "3" and prefix will remain unchanged.
|
||||||
|
|
||||||
So the first call will have prefix "tiles" (e.g.) and will save its image as
|
If quadrent is anything else, the tile will be saved just the same, but for
|
||||||
"tiles/base.png"
|
recursive calls a directory named quadrent will be created (if it doesn't
|
||||||
The second call will have prefix "tiles/0" and will save its image as
|
exist) and prefix will be set to os.path.join(prefix, quadrent)
|
||||||
"tiles/0.png"
|
|
||||||
The third call will have prefix "tiles/0/0" and will create directory
|
So the first call will have prefix "tiles" (e.g.) and quadrent "base" and
|
||||||
"tiles/0" to save its image as "tile/0/0.png"
|
will save its image as "tiles/base.png"
|
||||||
|
The second call will have prefix "tiles" and quadrent "0" and will save its
|
||||||
|
image as "tiles/0.png". It will create the directory "tiles/0/"
|
||||||
|
The third call will have prefix "tiles/0", quadrent "0" and will save its image as
|
||||||
|
"tile/0/0.png"
|
||||||
|
|
||||||
|
Each tile outputted is always 384 by 384 pixels.
|
||||||
"""
|
"""
|
||||||
pass
|
print "Called with {0},{1} {2},{3}".format(colstart, colend, rowstart, rowend)
|
||||||
|
print " prefix:", prefix
|
||||||
|
print " quadrent:", quadrent
|
||||||
|
cols = colend - colstart
|
||||||
|
rows = rowend - rowstart
|
||||||
|
|
||||||
|
if cols == 3 and rows == 5:
|
||||||
|
# base case: just render the image
|
||||||
|
img = render_worldtile(chunkmap, colstart, colend, rowstart, rowend)
|
||||||
|
elif cols < 3 or rows < 5:
|
||||||
|
Exception("Something went wrong, this tile is too small. (Please send "
|
||||||
|
"me the traceback so I can fix this)")
|
||||||
|
else:
|
||||||
|
# Recursively generate each quadrent for this tile
|
||||||
|
img = Image.new("RGBA", (384, 384))
|
||||||
|
|
||||||
|
# Find the midpoint
|
||||||
|
colmid = (colstart + colend) // 2
|
||||||
|
rowmid = (rowstart + rowend) // 2
|
||||||
|
if quadrent == "base":
|
||||||
|
# The first call has a special job. No matter the input, we need to
|
||||||
|
# make sure that each recursive call splits both dimensions evenly
|
||||||
|
# into a power of 2 * 384. (Since all tiles are 384x384 which is 3
|
||||||
|
# cols by 5 rows)
|
||||||
|
# Since the row of the final recursion needs to be 3, this split
|
||||||
|
# needs to be sized into the void so that it is some number of rows
|
||||||
|
# in the form 3*2^p. And columns must be in the form 5*2^p
|
||||||
|
# They need to be the same power
|
||||||
|
# In other words, I need to find the smallest power p such that
|
||||||
|
# colmid + 3*2^p >= colend and rowmid + 5*2^p >= rowend
|
||||||
|
for p in xrange(15): # That should be a high enough upper limit
|
||||||
|
if colmid + 3*2**p >= colend and rowmid + 5*2**p >= rowend:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise Exception("Your map is waaaay to big")
|
||||||
|
|
||||||
|
# Modify the lower and upper bounds to be sized correctly
|
||||||
|
colstart = colmid - 3*2**p
|
||||||
|
colend = colmid + 3*2**p
|
||||||
|
rowstart = rowmid - 5*2**p
|
||||||
|
rowend = rowmid + 5*2**p
|
||||||
|
|
||||||
|
print " power is", p
|
||||||
|
print " new bounds: {0},{1} {2},{3}".format(colstart, colend, rowstart, rowend)
|
||||||
|
|
||||||
|
newprefix = prefix
|
||||||
|
else:
|
||||||
|
# Assert that the split in the center still leaves everything sized
|
||||||
|
# exactly right by checking divisibility by the final row and
|
||||||
|
# column sizes. This isn't sufficient, but is necessary for
|
||||||
|
# success. (A better check would make sure the dimensions fit the
|
||||||
|
# above equations for the same power of 2)
|
||||||
|
assert (colmid - colstart) % 3 == 0
|
||||||
|
assert (colend - colmid) % 3 == 0
|
||||||
|
assert (rowmid - rowstart) % 5 == 0
|
||||||
|
assert (rowend - rowmid) % 5 == 0
|
||||||
|
|
||||||
|
newprefix = os.path.join(prefix, quadrent)
|
||||||
|
if not os.path.exists(newprefix):
|
||||||
|
os.mkdir(newprefix)
|
||||||
|
|
||||||
|
# Recurse to generate each quadrent of images
|
||||||
|
quad0file = generate_quadtree(chunkmap,
|
||||||
|
colstart, colmid, rowstart, rowmid,
|
||||||
|
newprefix, "0")
|
||||||
|
quad1file = generate_quadtree(chunkmap,
|
||||||
|
colmid, colend, rowstart, rowmid,
|
||||||
|
newprefix, "1")
|
||||||
|
quad2file = generate_quadtree(chunkmap,
|
||||||
|
colstart, colmid, rowmid, rowend,
|
||||||
|
newprefix, "2")
|
||||||
|
quad3file = generate_quadtree(chunkmap,
|
||||||
|
colmid, colend, rowmid, rowend,
|
||||||
|
newprefix, "3")
|
||||||
|
|
||||||
|
quad0 = Image.open(quad0file).resize((192,192))
|
||||||
|
quad1 = Image.open(quad1file).resize((192,192))
|
||||||
|
quad2 = Image.open(quad2file).resize((192,192))
|
||||||
|
quad3 = Image.open(quad3file).resize((192,192))
|
||||||
|
|
||||||
|
img.paste(quad0, (0,0))
|
||||||
|
img.paste(quad1, (192,0))
|
||||||
|
img.paste(quad2, (0, 192))
|
||||||
|
img.paste(quad3, (192, 192))
|
||||||
|
|
||||||
|
# Save the image
|
||||||
|
path = os.path.join(prefix, quadrent+".png")
|
||||||
|
img.save(path)
|
||||||
|
|
||||||
|
# Return its location
|
||||||
|
return path
|
||||||
|
|||||||
Reference in New Issue
Block a user