From BlenderWiki

Jump to: navigation, search

Ipo/IpoCurve API Changes

Motivation

The Ipo and Ipocurve API modules were missing quite a few useful features that I needed, so I decided to extend their functionality. I also wanted to give alternatives to methods which rely on the Ipo curve's or bezTriple's position in the Blender's internal Ipo or Ipocurve data structure, making access more object-oriented.

Summary of changes

  • Ipos use the [] mapping operator to access IpoCurves, similar to dictionaries
  • Ipos also support iteration over the defined IpoCurves
  • Ipos for materials, lamps and worlds can now access individual texture channels IpoCurves
  • IpoCurves use the [] index operator to get/set the curve's value at a specified time
  • new attributes comparable to getStuff()/setStuff() methods
  • IpoCurve.append() method, which accepts BezTriples or a tuple of floats
  • del operator support
  • IpoCurve allows assigning individual BezTriples to BezTriple sequences

Ipos

Ipo [] mapping operator

The curves in an Ipo are accessed using the [] mapping operator, similar to Python dictionaries. A slew of new Ipo constants are defined to access specific IpoCurves in the Ipo. If the specified curve is not defined for the Ipo, the getter function returns None. If the specified curve is not valid (i.e., the corresponding constant is out of range), a {{literal|KeyError} exception is thrown.

The code below demonstrates using the mapping operators. IpoCurves can also be created or deleted using item assignment.

from Blender import *
 
ipo = Ipo.Get('MatIpo')
ipo[Ipo.MA_R] = (5,.5)             # create a new IpoCurve with key at frame=5, value=.5
icu1 = ipo[Ipo.MA_R]               # icu1 is now the Red IpoCurve
ipo[Ipo.MA_G] = None               # delete an IpoCurve (if it exists)
icu2 = ipo[Ipo.MA_G]               # icu2 is None

The exception is for Key or Shape Ipos. These Ipos are handled in a very different way, having no particular adrcode value that corresponds to the Ipocurve name (users can rename Shape keys). If the Ipo is for a Key, the mapping operator takes a string argument:

from Blender import *
 
ipo = Ipo.Get('KeyIpo')
icu1 = ipo['Basis']         # icu1 is now the Basis IpoCurve (if it exists)

The in operator can also be used to test for a curve's existence:

from Blender import *
 
ipo = Ipo.Get('MatIpo')
if Ipo.MA_R in ipo:                # if Red IpoCurve exists...
   icu1 = ipo[Ipo.MA_R]            #   assign to icu1

Since the IpoCurve constants are not unique (i.e., the values of {{literal|OB_LOCX}{ and MA_R are the same) the in operator can be misused. The code below is functionally equivalent to the previous code:

from Blender import *
 
ipo = Ipo.Get('MatIpo')
if Ipo.OB_LOCX in ipo:                # if Red IpoCurve exists...
   icu1 = ipo[Ipo.OB_LOCX]            #   assign to icu1

You have been warned.

Ipo interator

The following code iterates over an Ipo's defined curves and prints their names:

ipo = Ipo.Get("MatIpo") for icu in ipo:   print icu.name

Ipo texture channel access

Material, Lamps, and Worlds can have up to ten texture channels assigned to them, each with their own set of IpoCurves. The Ipo API in Blender 2.41 does not allow easy access to these channels. For example, if two texture channels have a Col IpoCurve, the API method ipo.getCurves() returns a list of two Ipocurves which are each named "Col".

The new API adds a new attribute named channel which can get/set the texture channel value for Material, Lamp and World Ipos.

from Blender import *
 
ipo = Ipo.Get("MatIpo")
for channel in xrange(10):
  ipo.channel = channel
  if Ipo.MAP_COL in ipo:
    print 'map_col in channel',channel

Ipo attribute for determining valid curves (per-Ipo specific)

Each type of Ipo (Object, Material, Lamp, etc) supports a different, specific set of IpoCurves. It's desirable from a user's standpoint to have a quick mechanism for determining which IpoCurve constants are applicable for a particular Ipo. One way is to examine the Ipo constants and extract those which start with the correct prefix:

from Blender import *
 
print [id for id in Ipo.__dict__ if id[:3] == "OB_"]

The obvious disadvantage of this method is that it is tediuous for script writers. Instead, the new Ipo API will implement a curveConsts attribute which returns information describing the valid constants for the specified Ipo. There are some undecided issues about how this attribute should be implemented:

  • what should the attribute return: module constants are essentally dictionary keys in the module's dictionary. Should the attribute return a list of keys, a list of values, a dictionary of key/values pairs, or PyConstant? The proposed implementation would return a PyConstant, because it would allow the code to look similar to module constants:
from Blender import *
 
ipo = Ipo.Get('MatIpo')
curves = ipo.curveConsts
icu = ipo[curves.MA_R]

The disadvantage of this approach is PyConstants do not support iteration. It might be useful to have something which lets you iterate over all the possible curve types, not just those which are defined.

  • can the existing module constants be reused: Joseph suggested that "if you're returning constants loaded into the module dictionary and return some subset of this to the user you're simply passing him a reference to objects that already exist in the module's dictionary..." However, as far as I can tell there is no Python "dictionary item" type, only pairs of PyObjects for keys and values. Since the implementation so far uses a PyConstant instead this is less of an issue, but it does reuse the dictionary values. From a speed standpoint, however, looking up the values in the module's dictionary to reuse their constants is about 15% slower than just creating the new list of constants from scratch.

For a Key or Shape Ipo, this attribute returns a list of all defined strings.

Ipo attributes for Ipo curves

Antont requested implementing access to IpoCurves through Ipo attributes:

from Blender import *
 
ipo = Ipo.Get('MatIpo')
ipo.MA_R = (5,.5)             # create a new IpoCurve with key at frame=5, value=.5
icu1 = ipo.MA_R               # icu1 is now the Red IpoCurve
ipo.MA_G = None               # delete an IpoCurve (if it exists)
icu2 = ipo.MA_G               # icu2 is None

Since the attributes would be dependent on the Ipo type, there seem to be three ways to do this:

  • define a PyType for each Ipo type,
  • define derived classes for each Ipo base type,
  • fake it using a custom {{literal|get_attro()} function

The third option has the least impact on the code, but makes things a little messy since we want this to work in conjuntion with the nice tp_getsetter interface (which makes things like help(ipo) and dir(ipo) work). It can be made to work as much as possible (or as much as I can figure out) by having the custom get_attro() function

  • check for queries to strings matching curve names, and return the corresponding IpoCurve if it is defined or None
  • check for __dict__ queries and build return a extended type dictionary which includes all the curve names linked to a "stub" PyGetSetDef definition

This approach has been implemented but is currently disabled in the source. If you want to experiment with it, edit blender/source/blender/python/api2_2x/Ipo.c and uncomment the line below:

/* #undef CURVEATTRS   /* uncomment to enable curves as Ipo attributes */

IpoCurves

IpoCurve [] index operator

IpoCurves use the [] index operator to get/set the curve's value at a specified time. This is equivalent to the current ipo.EvaluateCurveOn() and icu.evaluate() methods.

from Blender import *
 
ipo = Ipo.Get('ObIpo')   # get an object Ipo
icu = ipo[Ipo.OB_LOCX]   # get the X location Ipo curve
value = icu[10.5]        # get the value at time 10.5
icu[11.5] = value + 0.5  # add larger value at time 11.5

IpoCurve attributes

Getsetter for two new attributes (extend and interpolation were added. Thes are equivalent to the methods which access the IpoCurve's extrapolation and interpolation settings.

IpoCurve methods

append() method, which accepts BezTriples or a tuple of floats


-- Ken Hughes