From f43498d033cdf046049f456dd3258181f2861112 Mon Sep 17 00:00:00 2001 From: Andrew Brown Date: Sun, 26 Feb 2012 20:19:26 -0500 Subject: [PATCH] Added a "crop" feature to render subsets of worlds --- docs/config.rst | 16 +++++++++++++ overviewer.py | 4 ++++ overviewer_core/settingsDefinition.py | 1 + overviewer_core/settingsValidators.py | 10 ++++++++ overviewer_core/tileset.py | 7 ++++-- overviewer_core/world.py | 34 +++++++++++++++++++++++++++ 6 files changed, 70 insertions(+), 2 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index 5b073dd..333fee3 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -334,6 +334,22 @@ values. The valid configuration keys are listed below. This is a where a specific texture pack can be found to be used during this render. It can be either a folder or a directory. Its value should be a string. +``crop`` + You can use this to render a small subset of your map, instead of the entire + thing. The format is (min x, max x, min z, max z). + + (The coordinates are block coordinates. The same you get with the debug menu + in-game) + + Example that only renders a 1000 by 1000 square of land around the origin:: + + renders['myrender'] = { + 'world': 'myworld', + 'title': "Cropped Example", + 'crop': (-500,500, -500,500), + } + + .. _customrendermodes: Custom Rendermodes and Rendermode Primitives diff --git a/overviewer.py b/overviewer.py index 83ee257..e8e5a86 100755 --- a/overviewer.py +++ b/overviewer.py @@ -365,6 +365,10 @@ 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 a crop is requested, wrap the regionset here + if "crop" in render: + rset = world.CroppedRegionSet(rset, *render['crop']) # If this is to be a rotated regionset, wrap it in a RotatedRegionSet # object diff --git a/overviewer_core/settingsDefinition.py b/overviewer_core/settingsDefinition.py index 4c3dd02..a38f5cc 100644 --- a/overviewer_core/settingsDefinition.py +++ b/overviewer_core/settingsDefinition.py @@ -73,6 +73,7 @@ renders = Setting(required=True, default={}, "texturepath": Setting(required=False, validator=validateTexturePath, default=None), "renderchecks": Setting(required=False, validator=validateInt, default=None), "rerenderprob": Setting(required=True, validator=validateFloat, default=0), + "crop": Setting(required=False, validator=validateCrop, default=None), # Remove this eventually (once people update their configs) "worldname": Setting(required=False, default=None, diff --git a/overviewer_core/settingsValidators.py b/overviewer_core/settingsValidators.py index 95a958d..7cf7cf8 100644 --- a/overviewer_core/settingsValidators.py +++ b/overviewer_core/settingsValidators.py @@ -166,6 +166,16 @@ def validateOutputDir(d): raise ValidationException("You must specify a valid output directory") return os.path.abspath(d) +def validateCrop(value): + if len(value) != 4: + raise ValidationException("The value for the 'crop' setting must be a tuple of length 4") + value = tuple(int(x) for x in value) + if value[0] >= value[1]: + raise ValidationException("About your crop numbers, the xmax value must be greater than the xmin value") + if value[2] >= value[3]: + raise ValidationException("About your crop numbers, the zmax value must be greater than the zmin value") + return value + def make_dictValidator(keyvalidator, valuevalidator): """Compose and return a dict validator -- a validator that validates each key and value in a dictionary. diff --git a/overviewer_core/tileset.py b/overviewer_core/tileset.py index e815907..6b97299 100644 --- a/overviewer_core/tileset.py +++ b/overviewer_core/tileset.py @@ -489,7 +489,7 @@ class TileSet(object): bounds = self._find_chunk_range() # Calculate the depth of the tree - for p in xrange(1,33): # max 32 + for p in xrange(2,33): # max 32 # Will 2^p tiles wide and high suffice? # X has twice as many chunks as tiles, then halved since this is a @@ -498,8 +498,11 @@ class TileSet(object): # Y has 4 times as many chunks as tiles, then halved since this is # a radius yradius = 2*2**p + # The +32 on the y bounds is because chunks are very tall, and in + # rare cases when the bottom of the map is close to a border, it + # could get cut off if xradius >= bounds.maxcol and -xradius <= bounds.mincol and \ - yradius >= bounds.maxrow and -yradius <= bounds.minrow: + yradius >= bounds.maxrow + 32 and -yradius <= bounds.minrow: break self.treedepth = p self.xradius = xradius diff --git a/overviewer_core/world.py b/overviewer_core/world.py index 603d4ff..51c5c6f 100644 --- a/overviewer_core/world.py +++ b/overviewer_core/world.py @@ -516,6 +516,40 @@ class RotatedRegionSet(RegionSetWrapper): x,z = self.rotate(x,z) yield x,z,mtime +class CroppedRegionSet(RegionSetWrapper): + def __init__(self, rsetobj, xmin, xmax, zmin, zmax): + super(CroppedRegionSet, self).__init__(rsetobj) + self.xmin = xmin//16 + self.xmax = xmax//16 + self.zmin = zmin//16 + self.zmax = zmax//16 + + def get_chunk(self,x,z): + if ( + self.xmin <= x <= self.xmax and + self.zmin <= z <= self.zmax + ): + return super(CroppedRegionSet, self).get_chunk(x,z) + else: + raise ChunkDoesntExist("This chunk is out of the requested bounds") + + def iterate_chunks(self): + return ((x,z,mtime) for (x,z,mtime) in super(CroppedRegionSet,self).iterate_chunks() + if + self.xmin <= x <= self.xmax and + self.zmin <= z <= self.zmax + ) + def get_chunk_mtime(self,x,z): + if ( + self.xmin <= x <= self.xmax and + self.zmin <= z <= self.zmax + ): + return super(CroppedRegionSet, self).get_chunk_mtime(x,z) + else: + return None + + + def get_save_dir(): """Returns the path to the local saves directory * On Windows, at %APPDATA%/.minecraft/saves/