From BlenderWiki

Jump to: navigation, search

-- KenHughes - 22 May 2005

BPython IPO/Curve Module Changes

This page discusses the changes being implemented in the Ipo and Curve modules. It affects the BPython types Ipo, Curve, Ipocurve, CurNurb, and BezTriple, and creates a new BNURBPoint type for NURB control points. As of this writing, the changes proposed below have been inplemented are are awaiting review and approval.

The major goals of these changes include:

  • making the API for Ipos and Curves as similar as possible
  • making the API for Ipocurves and CurNurbs as similar as possible
  • adding a type for NURB control points similar to the existing BezTriple type
  • begin the deprecation of older methods and attributes
  • add new methods and attributes where needed
  • provide an interface to MTex Ipo attributes (texture channels) for Material, World and Lamp objects

Implementation Plan

-- KenHughes - 25 Jun 2005

The implementation plan has been broken into the stages below, along with the current status. When patches are submitted to the tracker, the status will indicate this. The sections which follow describe in more detail the changes of that stage.

  • . Implement a BPoint module to wrap Blender NURB data. Status: posted to patch tracker (#2792), June 28, 2005
  • . Implement sequence and iterator operators, append/insert/delete in Ipocurve and CurNurb modules. Operators on BezTriples and BPoints. Status: posted to patch tracker (#2801), July 3, 2005
  • . Implement sequence and iterator operators in Ipo module. Operators on Ipocurves objects, same as Curves have for CurNurbs. Status: posted to patch tracker (#2807), July 6. 2005
  • . Implement dictionary constants for Ipos. This presents valid curve names (through module constants) which are used to get/append curves to Ipos. Status: posted to patch tracker (#2812), July 8 2005
  • . Implement access to texture channels IPO curves. This will use a "top-down" access through the material/lamp/world getTextures() method to return curves for a specific texture channel. Status: development in progress
  • . Add other methods to BPoint, BezTriple, CurNurb, Ipocurve, Curve and Ipo as needed. Status: development in progress
  • . Implement instance attributes for certain instance methods. Examine which methods in all modules should be replaced by tp_getset access. Status: to be done.

Stage 1. New BPoint module: posted to patch tracker (#2792), June 28, 2005

This modules implements a new BNURBPoint type and provides two instance methods: getPoint() and setPoint(). These methods give and take a sequence of 4 floats (x,y,z,w). There are also three instance attributes: pt: (read-only), the same thing as getPoint()*

  • tilt: (read/write), the point's tilt
  • weight: (read/write), the point's weight

The CurNurb methods which used a sequence of floats now use BNURBPoints, to make them consistent with BezTriples.

Stage 2. Changes to Ipocurve and CurNurb modules: posted to patch tracker (#2801), July 3, 2005

Ipocurves and CurNurbs now support access to their control points by sequence and iterator operators. Points can also be appended, inserted, and deleted from the lists. The point lists are treated as sequences, which also means that some Python slice operations are supported. Also, some of stiv's tp_getset operations have been implemented, so that attributes such as a CurNurb's mat_index or flagU can be read or written.

Here is an example of some of the new capabilities:

import Blender
curve = Blender.Curve.Get('curve')
curnurb = curve[0]
print len(curnurb), curnurb.flagU
if curnurb.isNurb: curnurb.append(Blender.BNURBPoint.New((0.0,0.0,0.0,0.1)))
for point in curnurb: print point
ipo = Blender.Ipo.Get('curve')
ipocurve = ipo.getCurve('LocX')
print ipocurve[0].getPoints()
del ipocurve[-1]
ipocurve2 = Blender.Ipo.Get('curve').getCurve('LocY')
if len(ipocurve) > 3: ipocurve2[0:2] = ipocurve[0:2]

Stage 3. Changes to Ipo and Curve module: posted to patch tracker (#2807), July 6, 2005

The index operator[] was added to the Ipo modules to access Ipocurves. Since Ipo curves have names they were implemented as mappings instead of sequences, hence the indexing is more like a dictionary. Append and delete operations were also implemented (the Curve module also). A lot of effort has also expended to begin deprecating methods which were redundant as a result of these changes. The deprecated methods have not been removed to ensure compatibility, instead printing some warnings to the console.

import Blender
curve = Blender.Curve.Get('curve')
del curve[-1]
curnurb = curve[0]
print len(curnurb), curnurb.flagU
if curnurb.isNurb: curnurb.append(Blender.BNURBPoint.New((0.0,0.0,0.0,0.1)))
for point in curnurb: print curve
ipo = Blender.Ipo.Get('curve')
print len(ipo)
ipocurve = ipo['LocX']
print ipocurve[0].getPoints()
del ipocurve[-1]
ipocurve2 = Blender.Ipo.Get('curve')['LocY']
if len(ipocurve) > 3: ipocurve2[0:2] = ipocurve[0:2]
del ipo['LocX']

Stage 4. Dictionary constants for Ipos: posted to patch tracker (#2812), July 8, 2005

One issue for Python programmers with Ipos (and many other modules) is knowing which curves are defined for each type of Ipo. Currently strings are used in the mapping operator[] (formerly getCurves()) and append() (formerly addCurve()) to specify the curve; however, there was no way within a BPython script to know which strings were valid. Dictionaries and module constants have been added to address this problem.

A new instance attribute types was added which returns a dictionary of module constants for the Ipo. Also, each of the curve types are available as a "virtual instance attribute" of the IPO (see example below) which make it simple for a script programmer to specify a curve.

import Blender
ipo = Blender.Ipo.Get('curve')
for name in ipo.types: if ipo[name] != None: print 'has curve ',name.value
LocX = ipo.types['LocX']
LocY = ipo.LocY
ipocurve = ipo[LocX]
ipocurve2 = Blender.Ipo.Get('curve')[LocY]
if len(ipocurve) > 3: ipocurve2[0:2] = ipocurve[0:2]
del ipo[LocX]

curve names as attrs, __getitem__ to replace .evaluate(time) ?

I also thought of dictionaries when this was discussed at the conference (2004), but agreed when someone proposed simply using instance attributes (they are in a dict too of course, the __dict__). Furthermore, have been wondering if the standard list access method would be good to use instead of curipo.evaluate(time). That would be something like this:

#gets the value of that curve at frame 10, equivalent of current ipocurve.evaluate(10)
#changes the value or adds a new control point
obipo.loc_x[20] = 15

Now how does that seem? Non-existing curves would evaluate as None.

-- ToniAlatalo - 16 Jun 2005

Stiv's comments from the BPython ML:

An interesting idea. However, I believe sometimes Ipos are evaluated between frames for things like fields and motion blur. This would mean float arguments to [], something python would complain about.

A better method might be to use the call operator() for evaluating the curve and use the index operator[] to get a sequence of control points. This would fit in with how Curves are implemented

-- KenHughes - 08 Jul 2005

how would setting keys/controlpoints work nicely then?

-- ToniAlatalo - 28 Oct 2005

Stage 5. Accessing texture channels IPO curves: development in progress

The texture channels are referenced inside Blender using by the constants MA_MAP1 through MA_MAP10, although the channel numbers displayed in the IPO curve editor window for materials, lamps and worlds are 0 through 9. Additionally, the curve names displayed in that window don't indicate which curves are the texture channels curves and which are not.

The problem with the current API

No distinction between texture channel curves and other curves. If two Col curves exist two different texture channels, this code:

import Blender
ipo = Blender.Ipo.Get('MatIpo')
print ipo.getCurves()

gives the output below, with way to discern which curve belongs to which texture channel since new curves are always appended to the end of list:

[[IpoCurve "Col"], [IpoCurve "Col"]]

Finally, when a curve is created using addCurve(), it is always added to the first texture channel.

A related comment; the new insertIpoKey() methods added to modules like Lamp, Material, etc., have a similar problem; they only allow adding to texture channel 0. They also need a way to select the texture channel.

Comments on Accessing texture channels

I wonder if the most intuitive way to add texture channel support is from the top down thru Materials. A Material has 10 Textures. A Texture can have an Ipo. Think of the Outliner display. Data access would be something like: myMaterial.texture[0].ipo

-- StephenSwaney - 04 Jun 2005

Blender database structure

Reading over this makes me think there is a fundamental misunderstanding of the blender database structure. Reading over this makes me think there is a fundamental misunderstanding of the blender database structure.

Blender uses an object structured database. Scene, Object, ObData ( like Mesh, Curve, etc), Material and Texture are all objects. A particular instance of an object can be linked to multiple parents. The same Object can be in multiple Scenes, the same Curve can be linked to multiple Objects. Similarly with a Texture being linked to multiple Materials. Ipos are not an exception to this.

Because of the linked object nature of the database, the *.Get() methods have a somewhat limited use. These methods return a specific instance of an object type, but they do not tell you which of the (possibly many) parent objects the returned type is linked to. This implies that the natural access to an instance is from the top down starting for example with an Object.

-- StephenSwaney - 10 Jul 2005

I don't have a problem getting rid of the *.Get() methods, but realize that a top-down access doesn't solve the problem of telling the user which other objects may also be linked to that Ipo.

Let me stress again that the texture channel are not part of the texture. Perhaps we should refer to them as the mtex objects, which again isn't accurate since it implies "Material Texture", but mtex's are also part of lamp and world objects. We may not like how that's implemented, but that's how it is.

-- KenHughes - 10 Jul 2005

The proposed solution

A "top-down" approach is being implemented to access texture channels from a material, lamp or world object; this is done using the BPython MTex type. Materials have a getTextures() method which returns a list of the 10 textures linked to the material (the current Bython Lamp and World API doesn't have this, but it will be added). An instance attribute ipo will be implemened which returns a "virtual" Ipo object which has access only to the texture channel curves for that particular Ipo type and channel.

An example: this code:

import Blender
myMat = Blender.Material.Get('Material')
mtex = myMat.getTextures()
# mtex = myMat.textures         # this will also work
if mtex[0] != None: ipo = mtex[0].ipo print 'ipo is ',ipo print 'keys are ',ipo.types.keys()

would output something like this (assuming the first texture channel has a texture):

ipo is  [Ipo "Material" (Material)]
keys are  ['Disp', 'OfsZ', 'OfsX', 'OfsY', 'SizeX', 'SizeY', 'SizeZ', 'texR', 'DefVar', 'Var', 'texG', 'Col', 'Nor', 'texB']

-- KenHughes - 25 Jun 2005

Stage 6. Adding other methods and attributes: development in progress

This is an open topic; what else would be useful?

  • jms has requested the ability to get/set the select status of an Ipo or curve
  • Toni had suggested an easy way to get the an Ipo curve's value at a particular time. Stiv's suggested using the call operator()
  • I'd like to be able to get/set the handle types of BezTriples, and also to change the cyclic setting of curNurbs. And a method to update an Ipo curve after the control points have changed

Methods vs Attributes: Our knowlege of Python has grown since the Bpy API was first done. Of course, Python itself has grown, too. One big change to Python is the blurring of differences between user defined types (like ours) and regular types and classes. Historically, we have used methods as the preferred way to access bpy type attributes. My personal preference is for accessing attributes directly ( myType.some_attr = 1 ) rather than thru methods ( myType.setSomeAttr(1) ).

-- StephenSwaney - 04 Jun 2005

Other issues -- KenHughes

Stivs, teeth and I had a discussion (Joseph also added some input about a similar problem with vector wrappers) on the Bf-python ML about what (if anything) should be done to keep changes to the point list of an Ipocurve or CurNurb from messing up any BPython BezTriple objects. I think we agreed there are some nasty things a Python programmer can do which they shouldn't, but that it would be better throw an exception at them instead of corrupted memory or a segfault.

The solution which has been implemented is for each BezTriple and BNURBPoint to check its parent curve to see (a) if the parent is a CurNurb or Ipocurve and (b) if the point's index in the curve list is larger than the list's current size. This is not fool-proof but better than nothing.

For those interested, the thread appears [[1]]

-- KenHughes - 19 Jun 2005