From e50cb2db88468dc94776471983485a3a3915f09d Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Sun, 26 Feb 2012 17:59:37 -0500 Subject: [PATCH] Generalized regionset wrapper objects so they can be chained --- overviewer.py | 5 ++- overviewer_core/world.py | 76 ++++++++++++++++++++++++++-------------- 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/overviewer.py b/overviewer.py index 0821a7b..83ee257 100755 --- a/overviewer.py +++ b/overviewer.py @@ -365,8 +365,11 @@ dir but you forgot to put quotes around the directory, since it contains spaces. if rset == None: # indicates no such dimension was found: logging.error("Sorry, you requested dimension '%s' for %s, but I couldn't find it", render['dimension'], render_name) return 1 + + # If this is to be a rotated regionset, wrap it in a RotatedRegionSet + # object if (render['northdirection'] > 0): - rset = rset.rotate(render['northdirection']) + rset = world.RotatedRegionSet(rset, render['northdirection']) logging.debug("Using RegionSet %r", rset) # create our TileSet from this RegionSet diff --git a/overviewer_core/world.py b/overviewer_core/world.py index 5e6e0c5..603d4ff 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -36,10 +36,10 @@ class BiomeDataDoesntExist(Exception): pass def log_other_exceptions(func): - """A decorator that prints out any errors that are not - ChunkDoesntExist or BiomeDataDoesntExist errors. This decorates - get_chunk because the C code is likely to swallow exceptions, so - this will at least make them visible. + """A decorator that prints out any errors that are not ChunkDoesntExist or + BiomeDataDoesntExist errors. This should decorate any functions or methods + called by the C code, such as get_chunk(), because the C code is likely to + swallow exceptions. This will at least make them visible. """ functools.wraps(func) @@ -265,7 +265,6 @@ class RegionSet(object): raise Exception("Woah, what kind of dimension is this! %r" % self.regiondir) # this is decorated with cache.lru_cache in __init__(). Be aware! - @log_other_exceptions def _get_biome_data_for_region(self, regionx, regionz): """Get the block of biome data for an entire region. Biome data is in the format output by Minecraft Biome Extractor: @@ -366,9 +365,6 @@ class RegionSet(object): return chunk_data - def rotate(self, north_direction): - return RotatedRegionSet(self.regiondir, north_direction) - def iterate_chunks(self): """Returns an iterator over all chunk metadata in this world. Iterates over tuples of integers (x,z,mtime) for each chunk. Other chunk data @@ -419,6 +415,33 @@ class RegionSet(object): x = int(p[1]) y = int(p[2]) yield (x, y, path) + +class RegionSetWrapper(object): + """This is the base class for all "wrappers" of RegionSet objects. A + wrapper is an object that acts similarly to a subclass: some methods are + overridden and functionality is changed, others may not be. The difference + here is that these wrappers may wrap each other, forming chains. + + In fact, subclasses of this object may act exactly as if they've subclassed + the original RegionSet object, except the first parameter of the + constructor is a regionset object, not a regiondir. + + This class must implement the full public interface of RegionSet objects + + """ + def __init__(self, rsetobj): + self._r = rsetobj + + def get_type(self): + return self._r.get_type() + def get_biome_data(self, x, z): + return self._r.get_biome_data(x,z) + def get_chunk(self, x, z): + return self._r.get_chunk(x,z) + def iterate_chunks(self): + return self._r.iterate_chunks() + def get_chunk_mtime(self, x, z): + return self._r.get_chunk_mtime(x,z) # see RegionSet.rotate. These values are chosen so that they can be # passed directly to rot90; this means that they're the number of @@ -428,7 +451,7 @@ UPPER_RIGHT = 1 ## - Return the world such that north is down the +X axis (rotat LOWER_RIGHT = 2 ## - Return the world such that north is down the +Z axis (rotate 180 degrees) LOWER_LEFT = 3 ## - Return the world such that north is down the -X axis (rotate 90 degrees clockwise) -class RotatedRegionSet(RegionSet): +class RotatedRegionSet(RegionSetWrapper): """A regionset, only rotated such that north points in the given direction """ @@ -440,32 +463,33 @@ class RotatedRegionSet(RegionSet): _ROTATE_180 = lambda x,z: (-x,-z) # These take rotated coords and translate into un-rotated coords - _unrotation_funcs = { - 0: _NO_ROTATION, - 1: _ROTATE_COUNTERCLOCKWISE, - 2: _ROTATE_180, - 3: _ROTATE_CLOCKWISE, - } + _unrotation_funcs = [ + _NO_ROTATION, + _ROTATE_COUNTERCLOCKWISE, + _ROTATE_180, + _ROTATE_CLOCKWISE, + ] # These translate un-rotated coordinates into rotated coordinates - _rotation_funcs = { - 0: _NO_ROTATION, - 1: _ROTATE_CLOCKWISE, - 2: _ROTATE_180, - 3: _ROTATE_COUNTERCLOCKWISE, - } + _rotation_funcs = [ + _NO_ROTATION, + _ROTATE_CLOCKWISE, + _ROTATE_180, + _ROTATE_COUNTERCLOCKWISE, + ] - def __init__(self, regiondir, north_dir): + def __init__(self, rsetobj, north_dir): self.north_dir = north_dir self.unrotate = self._unrotation_funcs[north_dir] self.rotate = self._rotation_funcs[north_dir] - super(RotatedRegionSet, self).__init__(regiondir) + super(RotatedRegionSet, self).__init__(rsetobj) - # Re-initialize upon unpickling + # Re-initialize upon unpickling. This is needed because we store a couple + # lambda functions as instance variables def __getstate__(self): - return (self.regiondir, self.north_dir) + return (self._r, self.north_dir) def __setstate__(self, args): self.__init__(args[0], args[1]) @@ -476,7 +500,7 @@ class RotatedRegionSet(RegionSet): def get_chunk(self, x, z): x,z = self.unrotate(x,z) - chunk_data = super(RotatedRegionSet, self).get_chunk(x,z) + chunk_data = dict(super(RotatedRegionSet, self).get_chunk(x,z)) chunk_data['Blocks'] = numpy.rot90(chunk_data['Blocks'], self.north_dir) chunk_data['Data'] = numpy.rot90(chunk_data['Data'], self.north_dir) chunk_data['SkyLight'] = numpy.rot90(chunk_data['SkyLight'], self.north_dir)