blender/intern/python/modules/Blender/Ipo.py
2002-10-12 11:37:38 +00:00

280 lines
7.9 KiB
Python

"""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