From 415c34935c58c05b5ca0e99a10198f871a90edf4 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 16 Jun 2011 21:40:18 -0600 Subject: Implement Progress class. --- .gitignore | 2 +- dimension/dimension.in | 34 +++++++++++++++++------ libdimension-python/Makefile.am | 4 ++- libdimension-python/dimension.pxd | 15 ++++++++++ libdimension-python/dimension.pyx | 58 +++++++++++++++++++++++++++++++++++---- libdimension-python/platform.c | 41 +++++++++++++++++++++++++++ libdimension-python/platform.h | 29 ++++++++++++++++++++ 7 files changed, 167 insertions(+), 16 deletions(-) create mode 100644 libdimension-python/platform.c create mode 100644 libdimension-python/platform.h diff --git a/.gitignore b/.gitignore index 98c0048..0b501ac 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,7 @@ Makefile.in /*/doc Doxyfile __pycache__ -libdimension-python/*.c +libdimension-python/dimension.c dimension/dimension # pkg-config files diff --git a/dimension/dimension.in b/dimension/dimension.in index dc243ab..1b6c229 100644 --- a/dimension/dimension.in +++ b/dimension/dimension.in @@ -21,6 +21,25 @@ import argparse import os.path +import sys + +# Display a progress bar +def progress_bar(str, progress): + if not _args.quiet: + print(str, end = ' ') + sys.stdout.flush() + + term_width = terminal_width() + width = term_width - (len(str) + 1)%term_width + for i in range(width): + progress.wait(i/width) + print('.', end = '') + sys.stdout.flush() + + print() + sys.stdout.flush() + + progress.finish() # Parse the command line _parser = argparse.ArgumentParser( @@ -74,6 +93,7 @@ die_on_warnings(_args.strict) # Defaults objects = [] lights = [] +camera = PerspectiveCamera() default_texture = Texture(finish = Ambient(0.1) + Diffuse(0.6)) default_interior = Interior() background = Black @@ -110,17 +130,15 @@ if _args.quality is not None: scene.quality = _args.quality # Raytrace the scene -if not _args.quiet: - if scene.nthreads == 1: - print("Rendering scene ...") - else: - print("Rendering scene (using %d threads) ..." % scene.nthreads) - -scene.raytrace() +if scene.nthreads == 1: + render_message = "Rendering scene" +else: + render_message = "Rendering scene (using %d threads)" % scene.nthreads +progress_bar(render_message, scene.raytrace_async()) # Write the output file export_timer = Timer() -canvas.write_PNG(_args.output) +progress_bar("Writing %s" % _args.output, canvas.write_PNG_async(_args.output)) export_timer.complete() # Print execution times diff --git a/libdimension-python/Makefile.am b/libdimension-python/Makefile.am index c1f5274..90d7d45 100644 --- a/libdimension-python/Makefile.am +++ b/libdimension-python/Makefile.am @@ -29,7 +29,9 @@ dimension.c: dimension.pyx dimension.pxd cython --line-directives dimension.pyx pyexec_LTLIBRARIES = dimension.la -dimension_la_SOURCES = dimension.c +dimension_la_SOURCES = dimension.c \ + platform.c \ + platform.h dimension_la_LDFLAGS = -avoid-version -module dimension_la_LIBADD = $(top_builddir)/libdimension/libdimension.la diff --git a/libdimension-python/dimension.pxd b/libdimension-python/dimension.pxd index b73b99c..6ac6319 100644 --- a/libdimension-python/dimension.pxd +++ b/libdimension-python/dimension.pxd @@ -64,6 +64,16 @@ cdef extern from "../libdimension/dimension.h": void dmnsn_array_remove(dmnsn_array *array, size_t i) void dmnsn_array_apply(dmnsn_array *array, dmnsn_callback_fn *callback) + ############ + # Progress # + ############ + + ctypedef struct dmnsn_progress + + int dmnsn_finish_progress(dmnsn_progress *progress) + double dmnsn_get_progress(dmnsn_progress *progress) + void dmnsn_wait_progress(dmnsn_progress *progress, double prog) + ########## # Timers # ########## @@ -178,6 +188,7 @@ cdef extern from "../libdimension/dimension.h": int dmnsn_png_optimize_canvas(dmnsn_canvas *canvas) int dmnsn_png_write_canvas(dmnsn_canvas *canvas, FILE *file) + dmnsn_progress *dmnsn_png_write_canvas_async(dmnsn_canvas *canvas, FILE *file) int dmnsn_gl_optimize_canvas(dmnsn_canvas *canvas) int dmnsn_gl_write_canvas(dmnsn_canvas *canvas) @@ -377,3 +388,7 @@ cdef extern from "../libdimension/dimension.h": void dmnsn_delete_scene(dmnsn_scene *scene) void dmnsn_raytrace_scene(dmnsn_scene *scene) + dmnsn_progress *dmnsn_raytrace_scene_async(dmnsn_scene *scene) + +cdef extern from "platform.h": + unsigned int dmnsn_terminal_width() diff --git a/libdimension-python/dimension.pyx b/libdimension-python/dimension.pyx index d5fbd1f..e1aba28 100644 --- a/libdimension-python/dimension.pyx +++ b/libdimension-python/dimension.pyx @@ -28,11 +28,53 @@ import os # Globals # ########### -# Make warnings fatal def die_on_warnings(always_die): """Whether to treat Dimension warnings as errors.""" dmnsn_die_on_warnings(always_die) +def terminal_width(): + """Return the width of the terminal, if present.""" + return dmnsn_terminal_width() + +############ +# Progress # +############ + +cdef class Progress: + cdef dmnsn_progress *_progress + + def __cinit__(self): + self._progress = NULL + + def __init__(self): + raise RuntimeError("attempt to create a Progress object.") + + def __dealloc__(self): + self.finish() + + def finish(self): + if self._progress != NULL: + try: + if dmnsn_finish_progress(self._progress) != 0: + raise RuntimeError("background task failed.") + finally: + self._progress = NULL + + def progress(self): + if self._progress == NULL: + raise RuntimeError("background task finished.") + return dmnsn_get_progress(self._progress) + + def wait(self, progress): + if self._progress == NULL: + raise RuntimeError("background task finished.") + dmnsn_wait_progress(self._progress, progress) + +cdef _Progress(dmnsn_progress *progress): + cdef Progress self = Progress.__new__(Progress) + self._progress = progress + return self + ########## # Timers # ########## @@ -107,7 +149,7 @@ cdef class Vector: elif args[0] == 0: self._v = dmnsn_zero else: - raise TypeError, "expected a sequence or 0" + raise TypeError("expected a sequence or 0") else: self._real_init(*args, **kwargs) @@ -471,14 +513,16 @@ cdef class Canvas: def write_PNG(self, path): """Export the canvas as a PNG file.""" + self.write_PNG_async(path).finish() + def write_PNG_async(self, path): + """Export the canvas as a PNG file, in the background.""" bpath = path.encode("UTF-8") cdef char *cpath = bpath cdef FILE *file = fopen(cpath, "wb") if file == NULL: raise OSError(errno, os.strerror(errno)) - if dmnsn_png_write_canvas(self._canvas, file) != 0: - raise OSError(errno, os.strerror(errno)) + return _Progress(dmnsn_png_write_canvas_async(self._canvas, file)) def draw_GL(self): """Export the canvas to the current OpenGL context.""" @@ -1317,11 +1361,13 @@ cdef class Scene: def raytrace(self): """Render the scene.""" + self.raytrace_async().finish() + def raytrace_async(self): + """Render the scene, in the background.""" # Ensure the default texture is complete cdef Texture default = Texture(Black) dmnsn_texture_cascade(default._texture, &self._scene.default_texture) - - dmnsn_raytrace_scene(self._scene) + return _Progress(dmnsn_raytrace_scene_async(self._scene)) def __dealloc__(self): dmnsn_delete_scene(self._scene) diff --git a/libdimension-python/platform.c b/libdimension-python/platform.c new file mode 100644 index 0000000..30dde93 --- /dev/null +++ b/libdimension-python/platform.c @@ -0,0 +1,41 @@ +/************************************************************************* + * Copyright (C) 2011 Tavian Barnes * + * * + * This file is part of The Dimension Python Module. * + * * + * The Dimension Python Module is free software; you can redistribute it * + * and/or modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Python Module is distributed in the hope that it will * + * be useful, but WITHOUT ANY WARRANTY; without even the implied * + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * + * the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +#include "platform.h" +#if HAVE_UNISTD_H + #include +#endif +#if DMNSN_TIOCGWINSZ + #include +#endif + +unsigned int +dmnsn_terminal_width(void) +{ + unsigned int width = 80; + +#if DMNSN_TIOCGWINSZ + struct winsize ws; + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0) { + width = ws.ws_col; + } +#endif + + return width; +} diff --git a/libdimension-python/platform.h b/libdimension-python/platform.h new file mode 100644 index 0000000..892217a --- /dev/null +++ b/libdimension-python/platform.h @@ -0,0 +1,29 @@ +/************************************************************************* + * Copyright (C) 2011 Tavian Barnes * + * * + * This file is part of The Dimension Python Module. * + * * + * The Dimension Python Module is free software; you can redistribute it * + * and/or modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Python Module is distributed in the hope that it will * + * be useful, but WITHOUT ANY WARRANTY; without even the implied * + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * + * the GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +#ifndef PLATFORM_H +#define PLATFORM_H + +/** + * Get the width of the terminal. + * @return The width (in characters) of the terminal, defaulting to 80. + */ +unsigned int dmnsn_terminal_width(void); + +#endif /* PLATFORM_H */ -- cgit v1.2.3