home *** CD-ROM | disk | FTP | other *** search
/ Hackers Magazine 57 / CdHackersMagazineNr57.iso / Software / Multimedia / k3d-setup-0.7.11.0.exe / lib / site-packages / cgkit / component.py < prev    next >
Encoding:
Python Source  |  2007-01-11  |  7.3 KB  |  233 lines

  1. # ***** BEGIN LICENSE BLOCK *****
  2. # Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3. #
  4. # The contents of this file are subject to the Mozilla Public License Version
  5. # 1.1 (the "License"); you may not use this file except in compliance with
  6. # the License. You may obtain a copy of the License at
  7. # http://www.mozilla.org/MPL/
  8. #
  9. # Software distributed under the License is distributed on an "AS IS" basis,
  10. # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. # for the specific language governing rights and limitations under the
  12. # License.
  13. #
  14. # The Original Code is the Python Computer Graphics Kit.
  15. #
  16. # The Initial Developer of the Original Code is Matthias Baas.
  17. # Portions created by the Initial Developer are Copyright (C) 2004
  18. # the Initial Developer. All Rights Reserved.
  19. #
  20. # Contributor(s):
  21. #
  22. # Alternatively, the contents of this file may be used under the terms of
  23. # either the GNU General Public License Version 2 or later (the "GPL"), or
  24. # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  25. # in which case the provisions of the GPL or the LGPL are applicable instead
  26. # of those above. If you wish to allow use of your version of this file only
  27. # under the terms of either the GPL or the LGPL, and not to allow others to
  28. # use your version of this file under the terms of the MPL, indicate your
  29. # decision by deleting the provisions above and replace them with the notice
  30. # and other provisions required by the GPL or the LGPL. If you do not delete
  31. # the provisions above, a recipient may use your version of this file under
  32. # the terms of any one of the MPL, the GPL or the LGPL.
  33. #
  34. # ***** END LICENSE BLOCK *****
  35. # $Id: component.py,v 1.3 2005/08/15 15:47:56 mbaas Exp $
  36.  
  37. ## \file component.py
  38. ## Contains the Component base class.
  39.  
  40. from _core import Component as _Component
  41. from Interfaces import ISceneItem
  42. import protocols, copy, inspect, re
  43. from slots import *
  44. import scene
  45.  
  46. # Component
  47. class Component(_Component):
  48.     """Base component class.
  49.  
  50.     Attributes:
  51.  
  52.     - name (\c str): The name of the component
  53.     """
  54.  
  55.     protocols.advise(instancesProvide=[ISceneItem])
  56.     
  57.     def __init__(self, name="", auto_insert=True):
  58.         _Component.__init__(self, name)
  59.         
  60.         if auto_insert:
  61.             scene.getScene().insert(self)
  62.  
  63.     def protocols(self):
  64.         return [ISceneItem]
  65.  
  66.     def connect(self, srcslot, dstslot):
  67.         dstslot.setController(srcslot)
  68.  
  69. ############
  70.  
  71. def _parseFuncDecl(funcdecl):
  72.     """Parse a function declaration string.
  73.  
  74.     The syntax of the string is like a C function declaration:
  75.  
  76.     <result_type> <func_name>(<parameters>)
  77.  
  78.     Example: "vec3 f(double x, double x, double z)"
  79.     
  80.     The function name may be left out
  81.  
  82.     The function returns a tuple (result_type, func_name, params)
  83.     where params is a list of tuples (name, type, default). The name of a
  84.     parameter might also be empty.
  85.     """
  86.     funcdecl = funcdecl.strip()
  87.  
  88.     # Parse the result type...
  89.     m = re.match("[a-zA-Z0-9]+ ", funcdecl)
  90.     if m==None:
  91.         raise SyntaxError, "No valid return type found"
  92.         
  93.     restype = funcdecl[m.start():m.end()].strip()
  94.  
  95.     # Parse the function name...
  96.     s = funcdecl[m.end():]
  97.     i = s.find("(")
  98.     if i==-1:
  99.         raise SyntaxError, "Parameter block is missing"
  100.     funcname = s[:i].strip()
  101.  
  102.     # Parse the arguments...
  103.     args = s[i+1:-1].split(",")
  104.     inputs = []
  105.     for s in args:
  106.         s = s.strip()
  107.         f = s.split()
  108.         if len(f)==1:
  109.             type = f[0]
  110.             name = ""
  111.         elif len(f)==2:
  112.             type,name = f
  113.         else:
  114.             raise SyntaxError, "Invalid parameter declaration: '%s'"%s
  115.         inputs.append((name,type,None))
  116.  
  117.     return restype, funcname, inputs
  118.     
  119. def _inspectFuncDecl(func):
  120.     """Determine the function declaration from the function itself.
  121.  
  122.     The function returns a tuple (result_type, func_name, params)
  123.     where params is a list of tuples (name, type, default).
  124.  
  125.     result_type and params may also be None if they cannot be determined
  126.     (i.e. if not every argument to the function has a default value).
  127.     """
  128.     # Extract function name...
  129.     funcname = func.__name__
  130.  
  131.     args,va,vkw,defaults = inspect.getargspec(func)
  132.     # Only proceed if every argument has a default value
  133.     if defaults==None or len(args)!=len(defaults):
  134.         return None, funcname, None
  135.  
  136.     # Extract the argument types (=the types of the default values)
  137.     inputs = []
  138.     for name, value in zip(args, defaults):
  139.         type = value.__class__.__name__
  140.         if type=="float":
  141.             type = "double"
  142.         inputs.append((name, type, value))
  143.  
  144.     # Determine the result type
  145.     v = func()
  146.     restype = v.__class__.__name__
  147.     if restype=="float":
  148.         restype = "double"
  149.     
  150.     return restype, funcname, inputs
  151.         
  152.  
  153. def _defaultValueLiteral(type, default):
  154.     """Convert a value into a string so that it can be executed again.
  155.     """
  156.     if default==None:
  157.         return ""
  158.     
  159.     if type in ["vec3", "vec4"]:
  160.         return "%s(%s)"%(type,default)
  161.     else:
  162.         return "%s"%default
  163.  
  164.  
  165. def createFunctionComponentSource(clsname, restype, funcname, inputs):
  166.     """Create a string that creates a component class.
  167.  
  168.     """
  169.  
  170.     res = """from cgkit import _core
  171. from cgkit.scene import getScene
  172.     
  173. class %s(Component):
  174.     
  175.     def __init__(self):
  176.         Component.__init__(self, func.__name__)
  177. """%(clsname)
  178.  
  179.     # Create output slot
  180.     outname = "output"
  181.     slotclassname = "Procedural%sSlot"%restype.capitalize()
  182.     slotname = "%s_slot"%outname
  183.     res += "        self.%s = %s(self.compute%s)\n"%(slotname, slotclassname, outname.capitalize())
  184.     res += '        self.addSlot("%s", self.%s)\n'%(outname, slotname)
  185.  
  186.     # Create input slots
  187.     for name, type, default in inputs:
  188.         slotclassname = "%sSlot"%type.capitalize()
  189.         slotname = "%s_slot"%name
  190.         res += "        self.%s = %s(%s)\n"%(slotname, slotclassname, _defaultValueLiteral(type, default))
  191.         res += '        self.addSlot("%s", self.%s)\n'%(name, slotname)
  192.         res += "        self.%s.addDependent(self.%s_slot)\n"%(slotname, outname)
  193.         if name=="time" and type=="double":
  194.             res += "        getScene().timer().time_slot.connect(self.%s)\n"%slotname
  195.  
  196. #    res += "        self._func_obj = %s\n"%funcname
  197.     res += "        self._func_obj = func\n"
  198.  
  199.     res += '\n    exec slotPropertyCode("%s")\n'%(outname)
  200.     for name,type,default in inputs:
  201.         res += '    exec slotPropertyCode("%s")\n'%(name)
  202.  
  203.     res += """\n    def compute%s(self):
  204.         return self._func_obj("""%(outname.capitalize())
  205.  
  206.     inames = map(lambda x: "%s=self.%s"%(x[0],x[0]), inputs)
  207.     res += ", ".join(inames)
  208.     res += ")\n"
  209.     
  210.     return res
  211.  
  212.  
  213. # createFunctionComponent
  214. def createFunctionComponent(func, funcdeclaration=None):
  215.  
  216.     if funcdeclaration==None:
  217.         restype, funcname, inputs = _inspectFuncDecl(func)
  218.         if restype==None:
  219.             raise ValueError, "Cannot determine the types of the arguments and the return value."
  220.     else:
  221.         restype, funcname, inputs = _parseFuncDecl(funcdeclaration)
  222.  
  223.     if funcname==None or funcname=="":
  224.         funcname = func.__name__
  225.     
  226.     clsname = funcname.capitalize()
  227.     s = createFunctionComponentSource(clsname, restype, funcname, inputs)
  228.     ns = copy.copy(globals())
  229.     ns["func"]=func
  230.     exec s in ns
  231.     return ns[clsname]
  232.     
  233.