Commits

Anonymous committed d990d89

Refactor: separate deposition operation from presentation

Comments (0)

Files changed (5)

heightfield/deposition.py

+import random
+from .surface import Surface
+
+
+ISLAND_CHAIN = [
+    # size, height, number
+    (100, 0.8, 200),
+    (75, 0.6, 300),
+    (50, 0.4, 400),
+    (25, 0.2, 500),
+]
+
+CONTINENTAL = [
+    (200, 0.7, 150),
+    (50, 0.5, 300),
+    (30, 0.2, 1000),
+]
+
+ISLANDS = [
+    (200, 1, 50),
+    (50, 0.3, 500),
+    (30, 0.1, 2000),
+]
+
+
+def deposit(landscape, octaves, base_height=0, progress_callback=None):
+    w = h = landscape.size
+    landscape.surface += base_height
+
+    total_area = 0.0
+    scaled_octaves = []
+    for size, height, number in octaves:
+        number = int(number * (w / 512.0) * (h / 512.0))
+        total_area += number * size * size
+        scaled_octaves.append((size, height, number))
+
+    passes = len(octaves)
+    progress = 0
+    for pass_, (size, height, number) in enumerate(scaled_octaves):
+        c = Surface.make_cone(size, height)
+
+        progress_step = (number // 5) * size * size
+        for j in xrange(5):
+            for _ in xrange(number // 5):
+                xpos = random.randint(-size, w)
+                ypos = random.randint(-size, h)
+                landscape.blit(c, xpos, ypos)
+            progress += progress_step
+            if progress_callback:
+                progress_callback(landscape, pass_, passes, progress / total_area)
+
+    progress_callback(landscape, passes, passes, 1)

heightfield/main.py

 import sys
-import random
 from .surface import Surface
+from .deposition import deposit, ISLANDS
+
+
+# default heightfield size
+SIZE = 768
+
+
+def text_progress_callback(surface, pass_, passes, fraction):
+    if fraction < 1:
+        sys.stdout.write('.')
+        sys.stdout.flush()
+    else:
+        print "finished."
+
+try:
+    import progressbar
+except ImportError:
+    get_cli_callback = lambda: text_progress_callback
+else:
+    def get_cli_callback():
+        widgets = [
+            '',
+            progressbar.Percentage(),
+            ' ',
+            progressbar.Bar(),
+            ' ',
+            progressbar.ETA()
+        ]
+        pbar = progressbar.ProgressBar(widgets=widgets).start()
+
+        def bar_progress_callback(surface, pass_, passes, fraction):
+            widgets[0] = 'Pass %d of %d: ' % (pass_, passes)
+            if fraction < 1:
+                pbar.update(fraction * 100)
+            else:
+                pbar.finish()
+        return bar_progress_callback
 
 
 def main():
         sys.exit("Usage: heightfield <output file>")
 
     outfile = sys.argv[1]
-    SIZE = 512
-    landscape = Surface(256, fill=-0.25)
+    landscape = Surface(SIZE)
 
-    for i in range(4):
-        d = 25 * i + 20
-        c = Surface.make_cone(d, (i + 1) * 0.2)
-        sys.stdout.write('.')
-        sys.stdout.flush()
-        for i in range(100 * (5 - i)):
-            xpos = random.randint(-d, SIZE)
-            ypos = random.randint(-d, SIZE)
-            landscape.blit(c, xpos, ypos)
-    print
-
+    callback = get_cli_callback()
+    deposit(landscape, ISLANDS, progress_callback=callback)
+    print "Writing %s..." % outfile
     landscape.to_pil().save(outfile)
 
 
-ISLAND_CHAIN = [
-    # size, height, number
-    (100, 0.8, 200),
-    (75, 0.6, 300),
-    (50, 0.4, 400),
-    (25, 0.2, 500),
-]
-
-CONTINENTAL = [
-    (200, 0.7, 150),
-    (50, 0.5, 300),
-    (30, 0.2, 1000),
-]
-
-ISLANDS = [
-    (200, 1, 50),
-    (50, 0.3, 500),
-    (30, 0.1, 2000),
-]
-
 def visible_deposition():
     from .viewer.pygameviewer import Viewer
     from pkg_resources import resource_stream
-    SIZE = 768
-    landscape = Surface(SIZE, fill=-0.25)
+    landscape = Surface(SIZE)
     viewer = Viewer(landscape, resource_stream(__name__, 'data/heightmap.png'))
     viewer.start()
-
-    octaves = ISLANDS
-
-    for size, height, number in octaves:
-        number = int(number * (SIZE / 512.0) ** 2)
-        c = Surface.make_cone(size, height)
-        sys.stdout.write('.')
-        sys.stdout.flush()
-        for j in xrange(5):
-            with viewer.lock:
-                for i in range(number // 5):
-                    xpos = random.randint(-size, SIZE)
-                    ypos = random.randint(-size, SIZE)
-                    landscape.blit(c, xpos, ypos)
-
-    print "finished."
+    deposit(landscape, ISLANDS, progress_callback=viewer.progress_callback)

heightfield/surface.py

 
 
 class Surface(object):
-    def __init__(self, size, fill=0):
+    def __init__(self, size):
         self.surface = zeros((size, size))
         self.size = size
         self.dirty = True
         h = img.size - sy
         dx2 = min(dx + w, self.size)
         dy2 = min(dy + h, self.size)
-        sw, sh = self.surface[dx:dx2,dy:dy2].shape
-        self.surface[dx:dx2,dy:dy2] += img.surface[sx:sx+sw,sy:sy+sh]
+        sw, sh = self.surface[dx:dx2, dy:dy2].shape
+        self.surface[dx:dx2, dy:dy2] += img.surface[sx:sx + sw, sy:sy + sh]
         self.dirty = True
 
     def to_pil(self):

heightfield/viewer/pygameviewer.py

                 finally:
                     self.screen.unlock()
 
+
+    def progress_callback(self, surface, pass_, passes, fraction):
+        self.repaint_rgb()
+
     def run(self):
         clock = pygame.time.Clock()
         while True:
             for event in pygame.event.get():
                 if event.type == pygame.QUIT:
                     sys.exit(0)
-            self.repaint_rgb()
             pygame.display.flip()
 import os
 from setuptools import setup, find_packages
 
-# Load long_description from README.rst
+# Load long_description from README
 readme = os.path.join(os.path.dirname(__file__), 'README')
 long_description = open(readme, 'rU').read().decode('utf8')
 
         'PIL>=1.1.6',
         'numpy>=1.5.1'
     ],
-    extras_require = {
-        'pygameviewer': ['pygame>=1.9']
+    extras_require={
+        'pygameviewer': ['pygame>=1.9'],
+        'progress': ['progressbar==2.2']
     },
     license='LGPL',
     classifiers=[