darcsden :: sfpotter -> pyray -> blob

root / pyray / io.py

import sys
import os
import subprocess

class File:
    """Class representing a .pov file."""

    def __init__(self, filename = "out.pov", *items):
        if filename[-4:] == ".pov":
            self.filename = filename[0:-4]
        else:
            self.filename = filename
        self.file = open(self.filename + ".pov", "w")
        self.__indent = 0
        self.iwrite(*items)

    def include(self, name):
        """Writes a POV-Ray include header.

        @type name: string
        @param name: the name of the POV-Ray include file, but without the
        suffix: C{f.include("colors")} => C{#include "colors.inc"}

        """
        
        self.writeln('#include "%s.inc"' % name)

    def declare(self, name, val):
        """Writes a POV-Ray declare header in the form C{#declare} I{name}
        C{=} I{val}.

        @type name: string
        @param name: the name to bind to
        @type val: I{varies}
        @param val: this can be anything that you would conceivable declare
        in POV-Ray, be it function, literal, primitive, etc.

        """
        
        self.write("#declare %s = " % name)
        if hasattr(val, "iwrite"):
            val.iwrite(self, False)
        else:
            self.writeln(str(val))

    def indent(self):
        """Increase the level of indentation."""
        
        self.__indent += 1

    def dedent(self):
        """Decrease the level of indentation."""

        self.__indent -= 1
        assert self.__indent >= 0

    def block_begin(self):
        """Opens a POV-Ray block in the file being written to."""
        
        self.writeln("{")
        self.indent()

    def block_end(self):
        """Closes a POV-Ray block in the file being written to."""
        
        self.dedent()
        self.writeln("}")
        if self.__indent == 0:
            self.writeln()

    def iwrite(self, *items):
        """Writes a variable number of Items to the file being written to.

        @type items: Item
        @param items: the items to be written

        """
        
        for item in items:
            item.iwrite(self)

    def write(self, s = "", indent = True):
        """Write a string to the file being written to.

        @type s: string
        @param s: the string to write
        @type indent: boolean
        @param indent: whether or not to indent the string at the current
        level of indentation

        """
        
        self.file.write(
            "".join(["  " * self.__indent if indent else "", s]))

    def writeln(self, s = "", indent = True):
        """Write a string to the file being written to and start a new line.

        @type s: string
        @param s: the string to write
        @type indent: boolean
        @param indent: whether or not to indent the string at the current
        level of indentation

        """
        
        self.file.write(
            "".join(["  " * self.__indent if indent else "",
                     s + os.linesep]))

    def close(self):
        """Close the file being written to."""

        self.file.close()

    def render(self,
               width,
               height,
               # quality = ?,
               # add in frame number, time, etc.? and include in the
               # non-verbose output message...?
               povray = None,
               output = None,
               location = None,
               verbose = True,
               clean = False,
               silent = True):
        """Close the file being written to (if necessary) and render the
        resultant file using the passed arguments.

        @type width: int
        @param width: The width in pixels at which to render
        @type height: int
        @param height: The height in pixels at which to render
        @type povray: string
        @param povray: An optionally provided path to a povray binary
        @type output: string
        @param output: The name of a file to render to. If not provided,
        .png will be appended to C{File:filename}
        @type location: string
        @param location: The directory to render to, defaults to the
        current directory
        @type verbose: boolean
        @param verbose: Whether or not to output the typical POV-Ray output
        information.
        @type clean: boolean
        @param clean: Whether or not to remove the .pov file after rendering
        has finished.
        @type silent: boolean
        @param silent: Suppress annoying beep following rendering
        completion.

        """
        
        if not self.file.closed:
            self.close()
        if povray is None:
            povray = "povray"
        if location is None:
            location = sys.path[0]
        if output is None:
            output = self.filename + ".png"
        output = "+O%s" % os.path.join(location, output)
        if not os.path.exists(location):
            os.mkdir(location)
        cmd = [povray,
               "+I%s" % self.filename + ".pov",
               output,
               "+W%d" % width,
               "+H%d" % height,
               "-p" if silent else ""]
        if clean:
            cmd.extend(["&&", "rm", self.filename + ".pov"])
        if verbose:
            out = os.popen(" ".join(cmd))
            for line in out.readlines():
                print(line)
        else:
            print("Rendering %s..." % self.filename)
            null = open(os.devnull, "w")
            subprocess.Popen(cmd,
                             stdout = null,
                             stderr = null)
            null.close()