0

RendertileSet: Round-robin the four top-level subtrees

This commit is contained in:
Johan Kiviniemi
2013-12-27 08:36:12 +02:00
parent e4638467ef
commit c49fdd19e1
2 changed files with 84 additions and 11 deletions

View File

@@ -25,7 +25,7 @@ import time
import errno import errno
import stat import stat
from collections import namedtuple from collections import namedtuple
from itertools import product, izip from itertools import product, izip, chain
from PIL import Image from PIL import Image
@@ -446,7 +446,7 @@ class TileSet(object):
# render. Iterate over the tiles in using the posttraversal() method. # render. Iterate over the tiles in using the posttraversal() method.
# Yield each item. Easy. # Yield each item. Easy.
if self.options['renderchecks'] in (0,2): if self.options['renderchecks'] in (0,2):
for tilepath in self.dirtytree.posttraversal(): for tilepath in self.dirtytree.posttraversal(robin=True):
dependencies = [] dependencies = []
# These tiles may or may not exist, but the dispatcher won't # These tiles may or may not exist, but the dispatcher won't
# care according to the worker interface protocol It will only # care according to the worker interface protocol It will only
@@ -1349,7 +1349,7 @@ class RendertileSet(object):
def __iter__(self): def __iter__(self):
return self.iterate() return self.iterate()
def iterate(self, level=None, offset=(0,0)): def iterate(self, level=None, robin=False, offset=(0,0)):
"""Returns an iterator over every tile in this set. Each item yielded """Returns an iterator over every tile in this set. Each item yielded
is a sequence of integers representing the quadtree path to the tiles is a sequence of integers representing the quadtree path to the tiles
in the set. Yielded sequences are of length self.depth. in the set. Yielded sequences are of length self.depth.
@@ -1362,6 +1362,9 @@ class RendertileSet(object):
In other words, specifying level causes the tree to be iterated as if In other words, specifying level causes the tree to be iterated as if
it was only that depth. it was only that depth.
If the `robin` parameter is True, recurses to the four top-level
subtrees simultaneously in a round-robin manner.
""" """
if level is None: if level is None:
todepth = 1 todepth = 1
@@ -1370,18 +1373,22 @@ class RendertileSet(object):
raise ValueError("Level parameter must be between 1 and %s" % self.depth) raise ValueError("Level parameter must be between 1 and %s" % self.depth)
todepth = self.depth - level + 1 todepth = self.depth - level + 1
return (tuple(path) for path in self._iterate_helper([], self.children, self.depth, onlydepth=todepth, offset=offset)) return (tuple(path) for path in self._iterate_helper([], self.children, self.depth, onlydepth=todepth, robin=robin, offset=offset))
def posttraversal(self, offset=(0,0)): def posttraversal(self, robin=False, offset=(0,0)):
"""Returns an iterator over tile paths for every tile in the """Returns an iterator over tile paths for every tile in the
set, including the explictly marked render-tiles, as well as the set, including the explictly marked render-tiles, as well as the
implicitly marked ancestors of those render-tiles. Returns in implicitly marked ancestors of those render-tiles. Returns in
post-traversal order, so that tiles with dependencies will always be post-traversal order, so that tiles with dependencies will always be
yielded after their dependencies. yielded after their dependencies.
"""
return (tuple(path) for path in self._iterate_helper([], self.children, self.depth, offset=offset))
def _iterate_helper(self, path, children, depth, onlydepth=None, offset=(0,0)): If the `robin` parameter is True, recurses to the four top-level
subtrees simultaneously in a round-robin manner.
"""
return (tuple(path) for path in self._iterate_helper([], self.children, self.depth, robin=robin, offset=offset))
def _iterate_helper(self, path, children, depth, onlydepth=None, robin=False, offset=(0,0)):
"""Returns an iterator over tile paths for every tile in the set.""" """Returns an iterator over tile paths for every tile in the set."""
# A variant of children with a collapsed False/True expanded to a list. # A variant of children with a collapsed False/True expanded to a list.
@@ -1395,10 +1402,16 @@ class RendertileSet(object):
if child: if child:
yield path + [childnum] yield path + [childnum]
else: else:
for (childnum, child), childoffset in distance_sort(enumerate(children_list), offset): gens = []
for (childnum_, child), childoffset_ in distance_sort(enumerate(children_list), offset):
if child: if child:
for p in self._iterate_helper(path + [childnum], children_list[childnum], depth-1, onlydepth=onlydepth, offset=childoffset): def go(childnum, childoffset):
yield p for p in self._iterate_helper(path + [childnum], children_list[childnum], depth-1, onlydepth=onlydepth, offset=childoffset):
yield p
gens.append(go(childnum_, childoffset_))
for p in roundrobin(gens) if robin else chain(*gens):
yield p
if onlydepth is None and children: if onlydepth is None and children:
yield path yield path

View File

@@ -1,6 +1,7 @@
import unittest import unittest
from overviewer_core.tileset import iterate_base4, RendertileSet from overviewer_core.tileset import iterate_base4, RendertileSet
from overviewer_core.util import roundrobin
class RendertileSetTest(unittest.TestCase): class RendertileSetTest(unittest.TestCase):
# If you change this definition, you must also change the hard-coded # If you change this definition, you must also change the hard-coded
@@ -196,6 +197,65 @@ class RendertileSetTest(unittest.TestCase):
self.assertRaises(StopIteration, next, iterator) self.assertRaises(StopIteration, next, iterator)
def test_posttraverse_roundrobin(self):
"""Test a round-robin post-traversal of the tree's dirty tiles"""
# Expect these results in this proper order.
expected_lists = [
[
(0,0,3),
(0,0,1),
(0,0,2),
(0,0,0),
(0,0),
(0,),
],
[
(1,2,0),
(1,2),
(1,0,3),
(1,0),
(1,1,3),
(1,1),
(1,),
],
[
(2,1,1),
(2,1,0),
(2,1,3),
(2,1,2),
(2,1),
(2,0,1),
(2,0,3),
(2,0,0),
(2,0,2),
(2,0),
(2,3,1),
(2,3,0),
(2,3,3),
(2,3,2),
(2,3),
(2,2,1),
(2,2,0),
(2,2,3),
(2,2,2),
(2,2),
(2,),
],
]
expected_list = list(roundrobin(expected_lists)) + [ () ]
iterator = iter(self.tree.posttraversal(robin=True))
from itertools import izip
for expected, actual in izip(expected_list, iterator):
self.assertEqual(actual, expected)
self.assertRaises(StopIteration, next, iterator)
def test_count_all(self): def test_count_all(self):
"""Tests getting a count of all tiles (render tiles plus upper tiles) """Tests getting a count of all tiles (render tiles plus upper tiles)