cleverplugs
aaa

Sugar API Documentation

“Sugar” is a new python programming interface for eStudio created by Clever Plugs in collaboration with Brainstorm, the makers of eStudio.

History

Up until very recently, the only way to program ipf was with the function itemset and itemget (together with itemnew, itemgo, itemcurve, etc.). While there is no limit to how much you can theoretically do with these functions, using them can get a bit longwinded. And, certainly if you recruit a python programmer, or learn some python, there is no synergy between python and these functions.

Both Brainstorm (specifically Jorge!) and Clever Plugs had the feeling a better interface was possible – one with some ‘syntactic sugar’. Jorge worked on a first version of this ‘sugar’ interface, and Cleverplugs meanwhile had independently created similar features in a framework during their work for Cheerful Scout.

Sugar2 is a ground-up creation based on Jorge’s concepts, making a programming interface to ipf (estudio) that is very “pythonic”, “object-oriented” and can be quite concise. The interface has been created “test-first”, meaning that there are full unit and regression tests for all its functions, and interestingly, there is also a version (a ‘mock’ or a ‘stub’) of sugar that can load outside of ipf (for testing scripts, for example).

Requirements and Installation

If you are running v11, you need to have a separate Python installation (not use the included python) and have set appropriate environment variables (reference needed to brainstorm site).

You need the sugar bundle, which is available as a zip archive

Download the bundle and put it somewhere (typically next to your db loader).

You need to create a new custom file which runs (PreLoad) sugar_loader.py.
Don’t move sugar_loader.py!

There is a useful sugar code editor (supporting drag and drop) implemented as an ipf folder (sugar_code_folder.py)
- this can be loaded (after sugar) as a separate custom file.

For simplicity when testing, you may want to use the alternative sugar_loader_with_stuff.py (instead of sugar_loader.py)
as this automatically creates a few visible GFX objs and opens the sugar_code_folder for you.
You can load this as if it was a database (on the command line after estudio).

First concepts

The basic way of accessing ipf is through the lists. There is an item called lists already available within ipf.

You can iterate through all the lists:

for l in lists: print l

This should give you an output including one for the obj list

lists.obj

Using the lists.obj, you can create new objects:

lists.obj.new()
lists.obj.new()

You can also grab the created item and assign it to a variable:

o = lists.obj.new()
print o

This should print something like

lists.obj['<dbs1>obj3']

You can then print all the objects you have created:

for o in lists.obj: print o
lists.obj['<dbs1>obj1']
lists.obj['<dbs1>obj2']
lists.obj['<dbs1>obj3']

These print-outs give an indication of how to access items. Now let’s go into more detail.

Creating items

When you create items you can create them with or without a name (and leave the naming to be the default name), and you can set values for one or more of their editors:

lists.obj.new('my new item')
lists.obj.new()
lists.obj.new('my displaced new item', displacement=(0,1,33), cull_tog=True)
lists.obj.new(displacement=(0,1,33))

Accessing items

You can access items by name, by order (the first one in the list is at index 0 – an old programming (including python) convention), and by ‘iteration’

print lists.obj['somename']
print lists.obj[0]
for o in lists.obj: print o.displacement

If your item is in the current database you can leave out the database name (but you can optionally include it). If it is in a different database, or in the globals, you need to specify the database as part of the name.

lists.obj["obj1"] #same as next
lists.obj["<dbs1>obj1"] 
lists.obj["<dbs2>obj1"] # different!
lists.obj["<>obj1"] # different!

Setting and getting editors

Given an item, you can set an editor on it, either at creation or later on

lists.obj.new('something', dispacement=(0,1,2))
o = lists.obj.new('something')
o.displacement = (0,1,2)
lists.obj['something'].displacement = (0,1,2)

Strictly speaking (and for maximum speed) you should actually use the method “set”:

o.displacement.set((0,1,2))
lists.obj['something'].displacement.set((0,1,2))

However sugar can usually infer the set for you.

The opposite of set() is get() – and again sugar can do a good job of guessing what you mean, but this occasionally can become a problem.

print lists.obj['obj1'].displacement.get()
print lists.obj['obj1'].displacement
filename = lists.obj['obj1'].prim.get().recglo_mat.get().selsurf.get().file.get()
filename = lists.obj['obj1'].prim.recglo_mat.selsurf.file.get()

Making actions and icons

To make actions, you can do the following, this is the equivalent of an “itemgo”:

o.displacement.makeAction((0,1,3), delay=0.5, transition=2).run()

You can separate them out:

action = o.displacement.makeAction((0,1,3), delay=0.5, transition=2)
action.run()

and for example keep them around so that you can do

action = o.displacement.makeAction((0,1,3), auto_delete=False, delay=0.5, transition=2)
action.run()

then, later

action.goto = 0

You can also create icons:

o.displacement.makeIcon()

By default this makes an IconEditor. If you want to make an IconCode (a button), you can do that:

o.displacement.makeIcon(value=(0,0,0), type=ipfconstants.ICON_TYPES_ICONCODE)

If you don’t provide a value, it will use the current value of the editor

Walking up and down the tree of parents and children

o = lists.obj.new('obj1')
c = lists.obj.new('obj1/child1')
g = lists.obj.new('obj1/child1/grandchild1')
lists.obj.new('obj1/child1/grandchild2')
lists.obj.new('obj1/child2')
lists.obj.new('obj1/child3')

You can “iterate” through them

for child in o.children:
  print child

You can access them by an index

print o.children[0]

Te parent editor is also useful

print g.parent.parent.get()
print g.parent.parent.parent.get()

You can use them to rearrange lists

g.parent = o
for child in o.children: print child
o.children = [g, c]
for child in o.children: print child

LSel, LString, LBell editors

LSel, LString and LBell editors support standard python len (length), iteration, and indexed access (but not assignment):

text = lists.text.new()
text.feeder_list = ["one", "two"]
print text.feeder_list[0] # ==> one
for s in text.feeder_list: print s
 # ==> one... two
print len(text.feeder_list) #==> 2
#works similarly for LSel (like children) and LBell editors

Editors’ names

You can predict the name of an editor:

Some editors have odd names, that either don’t match the rules (e.g. FVEL in the pivot list doesn’t start with PIVOT (not “PIVOT_FVEL”). Other editors have names that would yield illegal python names, e.g. 3dcursor (starts with a number, in text list).

For these, you can just use the full name of the editor IN CAPITALS, e.g.

lists.pivot['pivot1'].FVEL.get()
lists.text['text1'].TEXT_3DCURSOR.get()

For a del editor like OBJ_DEL, there’s a special case for this which is preferable:

pre(code).lists.obj[‘obj1’].DEL()

Easy access

You can easily get at the list item for a list:

lists.obj._listitem #==> lists.list['<>obj']
lists.obj._listitem.show = True
lists.obj['obj1']._list._listitem

You can also do the reverse, but this is an advanced feature, mostly used in plugin programming

lists['<>obj']._sugarlist['obj1']

Usefully, you can also iterate across all the editor items, or a specific editor item (i.e. a member of the editors list) for a list:

for editor in lists.obj._editors:
  print editor, editor.type
print lists.editor["<>OBJ_DISPLACEMENT"]
print lists.obj._editors.displacement
#same thing 

You can also get the instance to the editor item via a specific item in the original list:

print lists.obj['obj1'].displacement._editor
lists.obj['obj1'].displacement._editor.show()
lists.obj['obj1'].displacement._editor.managed = False
# same as 
print lists.obj._editors.displacement
lists.obj._editors.displacement.show()
lists.obj._editors.displacement.managed = False

Values and callbacks

Sometimes (particularly when writing new lists (plugins) for eStudio , but occasionally with normal ipf)
you want to set a value for an editor without doing any consequent changes – the “callbacks” of a plugin.

In the itemset/get API, you could modify the name of the editors by adding “~” or “!” (no callback, always callback),
but in sugar it is more clear, we believe:

#to set a value without a callback:  
lists.obj['obj1'].displacement._value = (0,0,1)
#to run the callback without changing the value:
lists.obj['obj1'].displacement._callback()

Future possibilities

not yet working!

Optimization for real-time speed

There is very little optimization of the code currently, and it is probably only appropriate for authoring. We should really be doing appropriate caching. However to do this effectively we need to use item ids rather than item names, which in itself is likely to do a speedup. This will require ctypes.

Indexed assignment and retrieval

o.displacement[2] = 0
print o.displacement[2]

Currently there’s no way to do indexed assignment, for retrieval you have to use get:

print o.displacement.get()[2]

A finder function:

for o in lists.obj.find(displacement=(1,2,0)): 
  print o

A comparator function?

print o1.diff(o2)
Differences between lists.obj['obj1'] and lists.obj['ob2']:
{
  displacement: ( (0,0,0), (0,1,2)),
  size: ( (1,1,1), (5,5,5))
}

An equals function?

if o.displacement.equals((0,0,0)): 
  print "no displacement"
if o.displacement.equals(o2.displacement):
  print "o and o2 have same displacement"

Currently you have to do:

if o.displacement.get() == (0,0,0): 
  print "no displacement"
if o.displacement.get() == o2.displacement.get():
  print "o and o2 have same displacement"

Advanced stuff: see advanced