"""The Blender Ipo module This module provides access to **Ipo** objects in Blender. An Ipo object is a datablock of IpoCurves which control properties of an object in time. Note that IpoCurves assigned to rotation values (which must be specified in radians) appear scaled in the IpoWindow (which is in fact true, due to the fact that conversion to an internal unit of 10.0 angles happens). Example:: from Blender import Ipo, Object ipo = Ipo.New('Object', 'ObIpo') # Create object ipo with name 'ObIpo' curve = ipo.addCurve('LocY') # add IpoCurve for LocY curve.setInterpolation('Bezier') # set interpolation type curve.setExtrapolation('CyclicLinear') # set extrapolation type curve.addBezier((0.0, 0.0)) # add automatic handle bezier point curve.addBezier((20.0, 5.0), 'Free', (10.0, 4.0)) # specify left handle, right auto handle curve.addBezier((30.0, 1.0), 'Vect') # automatic split handle curve.addBezier((100.0, 1.0)) # auto handle curve.update() # recalculate curve handles curve.eval(35.0) # evaluate curve at 35.0 ob = Object.get('Plane') ob.setIpo(ipo) # assign ipo to object """ import _Blender.Ipo as _Ipo import shadow _RotIpoCurves = ["RotX", "RotY", "RotZ", "dRotX", "dRotY", "dRotZ"] _radian_factor = 5.72957814 # 18.0 / 3.14159255 def _convertBPoint(b): f = _radian_factor newb = BezierPoint() p = b.pt q = newb.pt q[0], q[1] = (p[0], f * p[1]) p = b.h1 q = newb.h1 q[0], q[1] = (p[0], f * p[1]) p = b.h2 q = newb.h2 q[0], q[1] = (p[0], f * p[1]) return newb class IpoBlock(shadow.shadowEx): """Wrapper for Blender Ipo DataBlock Attributes curves -- list of owned IpoCurves """ def get(self, channel = None): """Returns curve with channel identifier 'channel', which is one of the properties listed in the Ipo Window, 'None' if not found. If 'channel' is not specified, all curves are returned in a list""" if channel: for c in self._object.curves: if c.name == channel: return IpoCurve(c) return None else: return map(lambda x: IpoCurve(x), self._object.curves) def __getitem__(self, k): """Emulates dictionary syntax, e.g. ipocurve = ipo['LocX']""" curve = self.get(k) if not curve: raise KeyError, "Ipo does not have a curve for channel %s" % k return curve def __setitem__(self, k, val): """Emulates dictionary syntax, e.g. ipo['LocX'] = ipocurve""" c = self.addCurve(k, val) has_key = get # dict emulation items = get # dict emulation def keys(self): return map(lambda x: x.name, self.get()) def addCurve(self, channel, curve = None): """Adds a curve of channel type 'channel' to the Ipo Block. 'channel' must be one of the object properties listed in the Ipo Window. If 'curve' is not specified, an empty curve is created, otherwise, the existing IpoCurve 'curve' is copied and added to the IpoBlock 'self'. In any case, the added curve is returned. """ if curve: if curve.__class__.__name__ != "IpoCurve": raise TypeError, "IpoCurve expected" c = self._object.addCurve(channel, curve._object) ### RotIpo conversion hack if channel in _RotIpoCurves: print "addCurve, converting", curve.name c.points = map(_convertBPoint, curve.bezierPoints) else: c.points = curve.bezierPoints else: c = self._object.addCurve(channel) return IpoCurve(c) _getters = { 'curves' : get } class BezierPoint: """BezierPoint object Attributes pt -- Coordinates of the Bezier point h1 -- Left handle coordinates h2 -- Right handle coordinates h1t -- Left handle type (see IpoCurve.addBezier(...) ) h2t -- Right handle type """ BezierPoint = _Ipo.BezTriple # override class IpoCurve(shadow.shadowEx): """Wrapper for Blender IpoCurve Attributes bezierPoints -- A list of BezierPoints (see class BezierPoint), defining the curve shape """ InterpolationTypes = _Ipo.InterpolationTypes ExtrapolationTypes = _Ipo.ExtrapolationTypes def __init__(self, object): self._object = object self.__dict__['bezierPoints'] = self._object.points def __getitem__(self, k): """Emulate a sequence of BezierPoints""" print k, type(k) return self.bezierPoints[k] def __repr__(self): return "[IpoCurve %s]" % self.name def __len__(self): return len(self.bezierPoints) def eval(self, time): """Returns float value of curve 'self' evaluated at time 'time' which must be a float.""" return self._object.eval(time) def addBezier(self, p, leftType = 'Auto', left = None, rightType = None, right = None): """Adds a Bezier triple to the IpoCurve. The following values are float tuples (x,y), denoting position of a control vertex: p -- The position of the Bezier point left -- The position of the leftmost handle right -- The position of the rightmost handle 'leftType', 'rightType' must be one of: "Auto" -- automatic handle calculation. In this case, 'left' and 'right' don't need to be specified "Vect" -- automatic split handle calculation. 'left' and 'right' are disregarded. "Align" -- Handles are aligned automatically. In this case, 'right' does not need to be specified. "Free" -- Handles can be set freely - this requires both arguments 'left' and 'right'. """ b = _Ipo.BezTriple() b.pt[0], b.pt[1] = (p[0], p[1]) b.h1t = leftType if rightType: b.h2t = rightType else: b.h2t = leftType if left: b.h1[0], b.h1[1] = (left[0], left[1]) if right: b.h2[0], b.h2[1] = (right[0], right[1]) self.__dict__['bezierPoints'].append(b) return b def update(self, noconvert = 0): # This is an ugly fix for the 'broken' storage of Rotation # ipo values. The angles are stored in units of 10.0 degrees, # which is totally inconsistent with anything I know :-) # We can't (at the moment) change the internals, so we # apply a conversion kludge.. if self._object.name in _RotIpoCurves and not noconvert: points = map(_convertBPoint, self.bezierPoints) else: points = self.bezierPoints self._object.points = points self._object.update() def getInterpolationType(self, ipotype): "Returns the Interpolation type - see also IpoCurve.InterpolationTypes" return self._object.getInterpolationType() def setInterpolationType(self, ipotype): """Sets the interpolation type which must be one of IpoCurve.InterpolationTypes""" try: self._object.setInterpolationType(ipotype) except: raise TypeError, "must be one of %s" % self.InterpolationTypes.keys() def getExtrapolationType(self, ipotype): "Returns the Extrapolation type - see also IpoCurve.ExtrapolationTypes" return self._object.getExtrapolationType() def setExtrapolationType(self, ipotype): """Sets the interpolation type which must be one of IpoCurve.ExtrapolationTypes""" try: self._object.setInterpolationType(ipotype) except: raise TypeError, "must be one of %s" % self.ExtrapolationTypes.keys() def New(blocktype, name = None): """Returns a new IPO block of type 'blocktype' which must be one of: ["Object", "Camera", "World", "Material"] """ if name: i = _Ipo.New(blocktype, name) else: i = _Ipo.New(blocktype) return IpoBlock(i) def Eval(ipocurve, time): # emulation code """This function is just there for compatibility. Use IpoCurve.eval(time) instead""" return ipocurve.eval(time) def Recalc(ipocurve): # emulation code """This function is just there for compatibility. Note that Ipos assigned to rotation values will *not* get converted to the proper unit of radians. In the new style API, use IpoCurve.update() instead""" return ipocurve.update(1) def get(name = None): """If 'name' given, the Ipo 'name' is returned if existing, 'None' otherwise. If no name is given, a list of all Ipos is returned""" if name: ipo = _Ipo.get(name) if ipo: return IpoBlock(ipo) else: return None else: return shadow._List(_Ipo.get(), IpoBlock) Get = get # emulation