<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>:: m a c a r o n i K a z o o ::</title>
	<atom:link href="http://www.macaronikazoo.com/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://www.macaronikazoo.com</link>
	<description>the zoo tools development blog</description>
	<lastBuildDate>Sun, 16 Sep 2012 22:50:37 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>serialization fun!</title>
		<link>http://www.macaronikazoo.com/?p=814</link>
		<comments>http://www.macaronikazoo.com/?p=814#comments</comments>
		<pubDate>Sun, 30 Oct 2011 00:16:28 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=814</guid>
		<description><![CDATA[I&#8217;ve been tinkering around with this little project for awhile now.  I&#8217;ve always wanted to try to write a simple little serialization format.  I know there solutions already out there &#8211; and in fact pyYAML seems to do everything mine does WAY better than I&#8217;ve managed. But thats not the point.  The point is I [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been tinkering around with this little project for awhile now.  I&#8217;ve always wanted to try to write a simple little serialization format.  I know there solutions already out there &#8211; and in fact <a href="http://pyyaml.org/" target="_blank">pyYAML</a> seems to do everything mine does WAY better than I&#8217;ve managed.</p>
<p>But thats not the point.  The point is I wanted to try and do it myself as an exercise. Its kinda tricky writing a nice serialization format and it was actually a fun little project.</p>
<p>Anyway, so what does it do?  Well, I wanted a text based serialization format that was easy to read and on the odd occasion hand edit.  You could write them by hand too if you&#8217;re feeling masochistic, but I strongly discourage it.  Having an easily readable, text based serialization format is terribly helpful for the occasions that you just want to crack the data open in an editor and see what the hell is going on.</p>
<p>Of course, you want to be able to write out interesting data too.  Which is obviously why pickle is incredibly appealing.  But pickle files are basically impossible for us mere mortals to read.  Plus they can also be abused by bad programmers (bad meaning incompetent or malicious).  I had a case recently where one of our tools was being hellishly slow.  So I spent a heap of time in a profiler trying to figure out why.  Turned out this particular tool, which was storing out data in pickle files, was pickling a database abstraction object.  The mere act of unpickling the file would cause a bunch of database activity.  In this particular case the tool was unpickling lots of these files which ended up taking a long time.  It took awhile to even bother looking at file loading as the culprit because the files themselves were only a few hundred bytes.</p>
<p>This is an easy mistake to make inadvertently in a language like python.</p>
<p>Pickle is amazingly cool.  Its also a VERY powerful tool to put in the hands of noobs and can sometimes cause headaches.</p>
<p>Anyway enough talking, lets look at some codez!</p>
<pre class="brush:py">import os
from maya import cmds
import sobject

objToSerialize = sobject.SObject()
objToSerialize.sceneName = cmds.file( q=True, sn=True )
objToSerialize.currentSelection = cmds.ls( sl=True )
objToSerialize.someRandomDict = { 'data': os.getenv( 'USER' ), 'cwd': os.getcwd() }
objToSerialize.subObj = subObj = sobject.SObject()
subObj.interestingNumbers = range(3)
subObj.cyclicReference = objToSerialize</pre>
<p>Kinda pointless data, but basically the objToSerialize object will be able to serialize itself to disk in a nice human readable way. The above code serialized to disk looks like this:</p>
<pre>42035216:
	sceneName(str):c:/somePath/someFile.ma
	currentSelection(list):
		(str):pSphere1
		(str):pCube1
	someRandomDict(dict):
		(str)data(NoneType):None
		(str)cwd(str):c:\Users\macaronikazoo\Documents\maya\scripts
	subObj(*):42035376:
		interestingNumbers(list):
			(int):0
			(int):1
			(int):2
		cyclicReference(*):42035216</pre>
<p>Thats fairly readable, right? Hardly a Dan Brown novel, but certainly easy enough to digest should you need to go poking around. And obviously if you need to get this information back into python, its as simple as passing either the string data to the Unserialize class method, or passing a filepath to the Load classmethod. Like so:</p>
<pre class="brush:py">objToSerialize.write( '~/sobject_test_file.txt' )
unserializedFromFile = sobject.Load( '~/sobject_test_file.txt' )  #unserialize from file
assert objToSerialize == unserializedFromFile</pre>
<p>Anyway &#8211; if you&#8217;re interested in taking a look at the code, feel free to! Its <a href="http://code.google.com/p/zootoolbox/source/browse/zooPy/sobject.py?name=dev" target="_blank">right here</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=814</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A Tidier Toolbox?</title>
		<link>http://www.macaronikazoo.com/?p=831</link>
		<comments>http://www.macaronikazoo.com/?p=831#comments</comments>
		<pubDate>Tue, 25 Oct 2011 10:45:24 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=831</guid>
		<description><![CDATA[I&#8217;ve been wanting to tidy up the toolbox for a LONG time now.  I&#8217;ve been slowly chipping away at the task the last few months. I think its finally in a decent enough shape to release.  So here it is. So whats new I hear you ask?  Well&#8230; Complete animation library re-write: It finally does [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been wanting to tidy up the toolbox for a LONG time now.  I&#8217;ve been slowly chipping away at the task the last few months. I think its finally in a decent enough shape to release.  So <a href="download/zooToolbox.7z">here it is</a>.</p>
<p>So whats new I hear you ask?  Well&#8230;</p>
<ul>
<li><strong>Complete animation library re-write</strong>:  It finally does what I&#8217;ve always wanted it to do &#8211; saving out both local space animation data AND world space animation data.  Which isn&#8217;t useful all the time, but sometimes it can be a real lifesaver.  Plus because the tool works based on selection, you can load part of the clip in worldspace and the rest in local space.  The tool still works and looks exactly the same, but under the hood things are much nicer.</li>
<li><strong>XferAnim</strong> is also a complete re-write to slot in better with the new anim clip.  They both share a lot more code now.</li>
<li><strong>Easier install</strong>:  There are no longer dagMenuProc scripts that you have to rename.  The toolbox <em>should</em> setup the dagMenuProc automatically when it loads.  There is even an option that will attempt to setup the toolbox to auto-load on maya start (although I haven&#8217;t tested this on very many setups yet so its hard to know how robust this feature is).</li>
</ul>
<p>So basically now all you should need to do is unzip the toolbox to your maya/scripts folder, startup maya and run the <strong>zooToolbox</strong> MEL command.</p>
<p>Enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=831</wfw:commentRss>
		<slash:comments>31</slash:comments>
		</item>
		<item>
		<title>Skeleton Builder Retrospective</title>
		<link>http://www.macaronikazoo.com/?p=810</link>
		<comments>http://www.macaronikazoo.com/?p=810#comments</comments>
		<pubDate>Sat, 15 Oct 2011 23:54:15 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=810</guid>
		<description><![CDATA[A recent g+ post from Christopher Evans prompted me to revisit the shortcomings of Skeleton Builder &#8211; my most recent public attempt at writing a rigging framework. I&#8217;d link to the g+ post, but sadly, its not public. I think in general Skeleton Builder is a really useful framework &#8211; it has certainly made me [...]]]></description>
			<content:encoded><![CDATA[<p>A recent g+ post from <a href="http://www.chrisevans3d.com/pub_blog/" target="_blank">Christopher Evans</a> prompted me to revisit the shortcomings of Skeleton Builder &#8211; my most recent public attempt at writing a rigging framework. I&#8217;d link to the g+ post, but sadly, its not public. I think in general Skeleton Builder is a really useful framework &#8211; it has certainly made me a more productive rigger in every single way. But its far from perfect, and often analyzing a project&#8217;s failures is the best way to learn so here we go.</p>
<p>Of course to look at failure we need to understand the initial goals first. Skeleton Builder (SB) is my second attempt at writing a rigging <em>framework</em>. Not an auto-rigger, although it is an auto-rigger.  But the part of the code I&#8217;m most interested in analyzing is the framework part. I&#8217;ll go into the difference in a bit. With SB I wanted to solve a bunch of problems.</p>
<ol>
<li>Skeleton construction and joint alignment. I wanted to make it easy to construct arbitrary skeletons without burdening the user <em>or the rigger</em>with joint alignment problems. I wanted consequent rigging code to be able to make guaranteed assumptions about joint alignment.</li>
<li>I wanted every skeleton part and every rig part to be introspectible from code. I wanted this so that rig parts could ask questions of other existing parts. This would allow them to be easily re-purposed and extended. It would also allow richer animation tools to be built upon them.</li>
<li>I wanted to be able to write a bunch of common code for a &#8220;limb&#8221; and have it re-used by biped arms, biped legs, quadruped legs, arachnid legs etc&#8230;  If not done via inheritance, then at least provide the same interfaces.</li>
<li>I wanted very little overhead to actually use the framework. I wanted to make it really easy to port existing rigging code over to use the SB framework.</li>
<li>And lastly I wanted to write a procedural UI to the framework so that non-technical users could use it and so rig coders didn&#8217;t need to write UI.</li>
</ol>
<p>So those were the major goals.</p>
<p>The first was was a mixed success. While the tool makes it really easy to build skeletons without having to worry about alignment for most things, its not immediately obvious to users that this is the case. Ideally the alignment would change in real-time as they&#8217;re making skeletal adjustments. Also joints like wrist joints still needed to be oriented manually. This wasn&#8217;t terribly obvious to users either, which is fair enough. As an end user when you see everything being automated, you don&#8217;t really stop to think whether there is anything you might need to pay attention to. So the tool should have made more of an effort to point out user intervention is required in this case.</p>
<p>The second point was also a mixed success.  SB has a bunch of helper functions in it to make coding space switching super easy.  Setting it up was generally a single line of code per control, or if you weren&#8217;t picky you could even use the  &#8221;auto space switching&#8221;. Space switching is amazingly useful and in fact, usually when I forget to put it on a control, I eventually hear back from an animator &#8211; &#8220;hey, can I get space switching on control X please?&#8221;.</p>
<p>Anyway the problem with space switching is that it is a confounding factor when you need to sort rig controls hierarchically. Space switching provides a way to effectively change the hierarchy. So without some way of taking this into account when you need to sort controls hierarchically, this can be a problem for a tool writer. So that in my opinion is a failure and I&#8217;m not even quite sure what the best solution is.  I mean its easy enough to write a function that takes space switching into account when doing a hierarchical sort, but how does a tool writer KNOW that they need to do this?</p>
<p>So lets go back to the bit about rigging frameworks vs auto-riggers.  Well the difference is basically architecture vs construction.  The framework is the environment the rigging code is written within.  When I started writing SB, I focused only on the framework side.  I had a pretty good idea what sort of functionality would be required, so I wrote that first.  Once I was happy with that, I took it out for a test drive by trying to write some actual rigging code using it.  This in turn pointed out some missing features in the framework which in turn caused me to re-write the actual rigging code etc&#8230;  This iteration cycle went on for a little bit and eventually out came the current incarnation of SB.</p>
<p>Basically a good auto-rigger should be built upon a good rigging framework.</p>
<p>Thats my brief analysis on the shortcomings of SB.  In general, like I said at the top, I think it has been very successful.  Its made me personally enormously more productive than I would been and it has been an amazingly enabling force for users of the tool as well.  But like I said, analyzing the failures is usually more instructive than analyzing the successes.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=810</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Coming soon</title>
		<link>http://www.macaronikazoo.com/?p=807</link>
		<comments>http://www.macaronikazoo.com/?p=807#comments</comments>
		<pubDate>Thu, 06 Oct 2011 10:13:45 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=807</guid>
		<description><![CDATA[I am still alive! Work is crazy at the moment so I haven&#8217;t had time to do much else.  But I have been chipping away at a few things.  I have a couple of posts half written that I will flesh out over the coming month.  And I actually have slowly been chipping away at [...]]]></description>
			<content:encoded><![CDATA[<p>I am still alive! Work is crazy at the moment so I haven&#8217;t had time to do <em>much</em> else.  But I have been chipping away at a few things.  I have a couple of posts half written that I will flesh out over the coming month.  And I actually have slowly been chipping away at the re-organization of the toolbox.  It&#8217;ll be a much cleaner install and hopefully it&#8217;ll be easier to leverage functionality contained within.</p>
<p>Anyway I&#8217;ll post more about it when I actually manage to ship it.  For now though, stay tuned.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=807</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>the importance of rig encapsulation</title>
		<link>http://www.macaronikazoo.com/?p=798</link>
		<comments>http://www.macaronikazoo.com/?p=798#comments</comments>
		<pubDate>Tue, 16 Aug 2011 21:34:15 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=798</guid>
		<description><![CDATA[Being able to write good animation tools relies on being able to easily make queries about a rig. Animation tools often need a high level understanding about how a rig works for many reasons. But obviously you want to maintain a loose coupling between the two. You want rigging to have the freedom to be [...]]]></description>
			<content:encoded><![CDATA[<p>Being able to write good animation tools relies on being able to easily make queries about a rig.  Animation tools often need a high level understanding about how a rig works for many reasons.  But obviously you want to maintain a loose coupling between the two.  You want rigging to have the freedom to be able to change the way the rigs work without having to worry about breaking animation tools.  Conversely you don&#8217;t want animation tools to be hamstrung by the lack of ability to encapsulate the complexity of the rig.</p>
<p>For example, is there a way to query the FK controls from a given IK control and vice-versa?  What about pole vector controls?  Is there a way to ask which controls have space switching?  If so can you query what the spaces are?  What about which controls are affected by a given space switch?  Given a joint can you get a list of the rig controls that drive it?  You get the idea.</p>
<p>Animation tools are basically a layer that build on top of the rig layer.  If the rig layer isn&#8217;t rock solid, then animation tools will be unstable or feature restricted or both.</p>
<p>Having some sort of programmatic interface to encapsulate the implementation details of your rig features is incredibly important if you want to be able to write useful and robust animation tools.  Without this sort of high level rig API you&#8217;ll most likely make it difficult or impossible to write the sort of tools that will enable your animators to be more productive.</p>
<p>So if you&#8217;re writing a rigging system, try taking a break from it and building some animation tools.  Exercise that rigging API you&#8217;ve been spending so much time on.  Better yet, use all the animation tools you write as part of your unit testing to validate changes made to your rigging API.  Remember, as a rigger your customers are both your animators and anyone who might write animation tools.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=798</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>more on OO rigging and animator tools</title>
		<link>http://www.macaronikazoo.com/?p=784</link>
		<comments>http://www.macaronikazoo.com/?p=784#comments</comments>
		<pubDate>Thu, 21 Jul 2011 22:47:08 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=784</guid>
		<description><![CDATA[Following on from the previous post I thought I&#8217;d talk a bit about how you could implement a tool which would display a bunch of actions that are supported by the currently selected rig parts. Now there are a bunch of different ways you can do this&#8230; This is just one way that seems (to [...]]]></description>
			<content:encoded><![CDATA[<p>Following on from the <a title="object oriented rigging in Skeleton Builder" href="http://www.macaronikazoo.com/?p=752">previous post</a> I thought I&#8217;d talk a bit about how you could implement a tool which would display a bunch of actions that are supported by the currently selected rig parts.</p>
<p>Now there are a bunch of different ways you can do this&#8230;  This is just one way that seems (to me) like a good combination of rigor and ease of coding.</p>
<p>In Skeleton Builder, each rig part is encapsulated in a class that derives from RigPart.  The next step is to create an <a title="A more thorough Interface class" href="http://www.macaronikazoo.com/?p=726">interface class</a> which defines two methods &#8211; switchToFk and switchToIk.  Our rig part class will inherit from both RigPart and this interface class and will implement these two methods.  Our code ends up looking something like this:</p>
<pre class="brush:py">import typeFactories
import baseRigPrimitive
class IkFkSwitchable(object):
  __metaclass__ = typeFactories.interfaceTypeFactory( baseRigPrimitive.RigPart.__metaclass__ )
  def switchToFk( self ):
    '''this method should implement the logic to switch to FK mode'''
    pass
  def switchToIk( self ):
    '''this method should implement the logic to switch to IK mode'''
    pass

class Leg(baseRigPrimitive.RigPart, IkFkSwitchable):
  #the code to implement this rig part goes here
  ...
  def switchToFk( self ):
    #implementation of this method goes below...
    ...
  def switchToIk( self ):
    #implementation of this method goes below...
    ...</pre>
<p>As you can see here, the Leg rig part class implements the logic for switchToFk and switchToIk (otherwise an exception gets thrown).  Now we can write a simple UI with a &#8220;switch to Fk&#8221; button and a &#8220;switch to Ik&#8221; button, something like this:</p>
<pre class="brush:py">import rigPrimitives
import maya.cmds as cmd
from baseMelUI import *

def getSelectedSwitchableParts():
  '''
  returns a list of all RigPart instances that have at least one of their members
  selected
  '''
  selectedSwitchableParts = []
  selectedRigParts = rigPrimitives.getPartsFromObjects( cmd.ls( sl=True ) )
  for rigPart in selectedRigParts:
    if isinstance( rigPart, IkFkSwitchable ):
      selectedSwitchableParts.append( rigPart )

  return selectedSwitchableParts

class UserToolLayout(MelHLayout):
  def __init__( self, parent ):
    MelHLayout.__init__( self, parent )
    self.UI_fkButton = MelButton( self, l="switch to FK", c=self.on_pressFk )
    self.UI_ikButton = MelButton( self, l="switch to IK", c=self.on_pressIk )

    #this useful method sets up a scriptjob parented to the uI that triggers the given
    #method when the scene selection changes.  So this will update the UI when the user
    #changes the selection
    self.setSelectionChangeCB( self.on_selectionChange )
    self.on_selectionChange()  #call this to init the UI

    #make sure to call this so the form layout gets setup properly
    self.layout()

  ### EVENT HANDLERS ###
  def on_selectionChange( self, *a ):
    #need to explicitly cast here because of a bug in setEnabled
    selectedSwitchableParts = bool( getSelectedSwitchableParts() )
    self.UI_fkButton.setEnabled( selectedSwitchableParts )
    self.UI_ikButton.setEnabled( selectedSwitchableParts )
  def on_pressFk( self, *a ):
    for rigPart in getSelectedSwitchableParts():
      rigPart.switchToFk()
  def on_pressIk( self, *a ):
    for rigPart in getSelectedSwitchableParts():
      rigPart.switchToIk()

class UserToolWindow(BaseMelWindow):
  WINDOW_NAME = "ikFkSwitcherTool"
  WINDOW_TITLE = "Ik Fk switcher tool"
  DEFAULT_SIZE = 300, 45
  DEFAULT_MENU = None

  def __init__( self ):
    UserToolLayout( self )
    self.show()

UserToolWindow()</pre>
<p>So this super simple little tool will update itself on selection change.  It will let the user know whether the selected rig part can be switched from Fk to Ik or vice-versa.</p>
<p>Obviously you could make this a hell of a lot smarter.  For example, you could inspect the rig parts, see which interfaces they implement and display the appropriate UI.  You could also do things like have the IkFkSwitchable interface class define a getIkFkMode() method which would return whether the rig part is in Fk or Ik mode.  This way you could only enable the appropriate button to make it even more obvious to the user the range of available actions.</p>
<p>Anyway hopefully you get the idea.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=784</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>yay for g+</title>
		<link>http://www.macaronikazoo.com/?p=780</link>
		<comments>http://www.macaronikazoo.com/?p=780#comments</comments>
		<pubDate>Mon, 18 Jul 2011 03:25:20 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=780</guid>
		<description><![CDATA[Never managed to get into facebook. Within hours of signing up I had friend requests from all sorts of crazy people that I either barely knew, or knew but certainly wasn&#8217;t keen or comfortable sharing various aspects of my life with etc&#8230; I&#8217;m sure the story is familiar. So it just didn&#8217;t take with me. [...]]]></description>
			<content:encoded><![CDATA[<p>Never managed to get into facebook. Within hours of signing up I had friend requests from all sorts of crazy people that I either barely knew, or knew but certainly wasn&#8217;t keen or comfortable sharing various aspects of my life with etc&#8230; I&#8217;m sure the story is familiar. So it just didn&#8217;t take with me.</p>
<p>Anyhoo, g+ seems like its solved all of these problems in a way that makes complete sense. And its google. I manage all my photos with picasa because its just bloody easy. And I LOVE gReader. So anyway, hopefully it&#8217;ll be awesome. If you&#8217;re not using it, try it out. Look for <a href="https://plus.google.com/108847499546939618060/posts" target="_blank">macaronikazoo</a>!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=780</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>object oriented rigging in Skeleton Builder</title>
		<link>http://www.macaronikazoo.com/?p=752</link>
		<comments>http://www.macaronikazoo.com/?p=752#comments</comments>
		<pubDate>Sun, 17 Jul 2011 15:47:03 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=752</guid>
		<description><![CDATA[Skeleton builder is a neat framework for writing rigging code, but its also a useful tool for writing runtime tools as well.  I figured I&#8217;d go over an example of this because its not terribly obvious until you actually get in there and write some code. And even then, its not terribly obvious until you [...]]]></description>
			<content:encoded><![CDATA[<p>Skeleton builder is a neat framework for writing rigging code, but its also a useful tool for writing runtime tools as well.  I figured I&#8217;d go over an example of this because its not terribly obvious until you actually get in there and write some code. And even then, its not terribly obvious until you read all the code written in baseRigPrimitive&#8230;</p>
<p>Writing a tool to do seamless Ik/Fk switching on an arbitrary rig is basically impossible.  After all, your particular limb setup might be a simple biped leg, a 5 jointed spider type leg, or a quadruped leg or any other crazy scenario.  Writing a single function to deal with all these possibilities, especially when you factor in the different ways these rigs could be implemented is basically impossible.</p>
<p>But writing the logic to do the switch for any of them in isolation is generally dead easy.  This is the sort of problem that object oriented programming was invented to solve.  In Skeleton Builder the way I solve these problems is like this.</p>
<p>In Skeleton Builder you write rig primitives. Each rig primitive is written to rig a particular sub-section of a skeleton and the code to do so is implemented in its own class.  So using the above examples I would have 3 classes: BipedLegRig, SpiderLegRig and QuadrupedLegRig.  Now on each one of these classes, lets define a switchToIk() method and a switchToFk() method.  So the switching logic for each rig type can be implemented independently on each of these classes.</p>
<p>Your code will look something like this:</p>
<pre class="brush:py">import rigPrimitives

class SpiderLegRig(rigPrimitives.RigPart):
  #this class variable defines the alias' for the controls. controls are accessed using these alias'
  CONTROL_NAMES = 'legControl', 'fkThigh', 'fkKnee', 'fkAnkle'

  def _build( self, skeletonPart, **kw ):
    #the code to build the actual rig goes here
    ...

    #this is the list of nodes that will get connected to the alias' defined
    #in the CONTROL_NAMES class variable
    controls = ikControl, fk0, fk1, fk2
    namedNodes = ()

    return controls, namedNodes
  def switchToIk( self ):

    #this code grabs the actual control nodes based on their alias'
    legControl = self.getControl( 'legControl' )
    fkThigh = self.getControl( 'fkThigh' )
    fkKnee = self.getControl( 'fkKnee' )
    fkAnkle = self.getControl( 'fkAnkle' )

    #logic to switch from Fk to Ik goes below
    ...
  def switchToFk( self ):
    #logic to switch from Ik to Fk goes below
    ...</pre>
<p>Then calling this switch method is as simple as doing something like this:</p>
<pre class="brush:py">from maya import cmds

selectedControls = cmds.ls( sl=True )
if selectedControls:
  rigPart = rigPrimitives.RigPart.InitFromItem( selectedControls[0] )
  rigPart.switchToFk()</pre>
<p>And voila. So now you can write a single UI that lets users switch between Ik and Fk for ANY part of the rig simply by running the above code.</p>
<p>I&#8217;ll write up another post with another example of a useful runtime tool that I&#8217;ve written that would be very difficult to do without a nice object oriented rigging framework to leverage.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=752</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>dependencies tool</title>
		<link>http://www.macaronikazoo.com/?p=764</link>
		<comments>http://www.macaronikazoo.com/?p=764#comments</comments>
		<pubDate>Wed, 13 Jul 2011 05:36:32 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=764</guid>
		<description><![CDATA[I finally managed to package up the dependency tracking tool I blogged about ages ago.  If you want to take a squiz you can find it here.  Its kinda neat actually.  As you can see from the link to the tool &#8211; its a zip file.  I made this zip file using one of the [...]]]></description>
			<content:encoded><![CDATA[<p>I finally managed to package up the dependency tracking tool I <a title="Finding Dependencies" href="http://www.macaronikazoo.com/?p=329">blogged about ages ago</a>.  If you want to take a squiz you can <a href="http://www.macaronikazoo.com/download/pyDeps.zip" target="_blank">find it here</a>.  Its kinda neat actually.  As you can see from the link to the tool &#8211; its a zip file.  I made this zip file using one of the features of the tool; the /package flag.  Because the tool knows all the dependencies of all the scripts in your tree, you can ask it to make a zip file containing all the dependencies for a given list of scripts.</p>
<p>So to make the zip file in the link above I ran this command:</p>
<pre>pydeps /package pydeps.cmd</pre>
<p>And that was it.  Simple eh?  Packaging isn&#8217;t terribly useful, but I figured I&#8217;ll most likely use this tool to generate future zooToolBox distributions.  I should be able to maintain a list of tools I want to include and pipe them into this command.  And it seemed like a useful thing for anyone who wants to distribute a neat tool without having to manually figure out what they need to include.</p>
<p>You may be wondering about the .cmd extension in the example above.  Well take a look at the zip file.  Basically .cmd files in windows are similar to shell scripts in *nix.  The tool will peek into a .cmd file to see if its a python script and if so, will include them in the dependency database.</p>
<p>So what else can you do with it?  The main features are the /i and the /d flags.  The /i flag will report all import dependencies for the given scripts.  By default it will report only immediate import dependencies, but you can pass it an depth integer as well.  For example if you wanted to know every single import dependency for a script (ie what the imported scripts import etc), you&#8217;d simply run this command:</p>
<pre>pydeps /i 0 someScript.py</pre>
<p>The /d flag will report scripts that are dependent on the ones you specify.  So just say you find some code you want to re-factor.  You can run this command:</p>
<pre>pydeps /d 1 someScript.py</pre>
<p>This command will report deep dependents for someScript.py.  By this I mean it will report every script that may possibly be using some part of someScript.py.  So for example, just say you import someScript into scriptA and then import scriptA into scriptB.  ScriptB will have access to code in someScript via its scriptA import right?  So now the pyDeps tool will report scriptA as an immediate dependent and scriptB as a deep dependent when run on someScript.</p>
<p>The other flag worth pointing out is the /tree flag.  This will print out a dependency tree for the given scripts.  This can be an interesting exercise &#8211; try running it on some of your tools.  I expect you&#8217;ll see some cross dependencies that you weren&#8217;t expecting.  It&#8217;d be kinda neat to do some big fat visualization graph of this&#8230;  I did think about this &#8211; in fact I figured if I bothered doing it, I&#8217;d just use maya&#8217;s hypergraph.  Build some nodes to reflect the scripts and connect em up.  If anyone ends up doing this &#8211; let me know!</p>
<p>Anyway, there are a few other flags &#8211; just run the tool without args to see the available flags.  Enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=764</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>cleaning house</title>
		<link>http://www.macaronikazoo.com/?p=762</link>
		<comments>http://www.macaronikazoo.com/?p=762#comments</comments>
		<pubDate>Tue, 12 Jul 2011 00:22:15 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=762</guid>
		<description><![CDATA[I figured I&#8217;d try to clean up the toolbox.  Its basically a horrible mess of crap all in the top level directory.  I&#8217;ve wanted to have this done for a long time, but so far my strategy of leaving it to magically fix itself has failed to yield results.  :( Anyway I wanted to get [...]]]></description>
			<content:encoded><![CDATA[<p>I figured I&#8217;d try to clean up the toolbox.  Its basically a horrible mess of crap all in the top level directory.  I&#8217;ve wanted to have this done for a long time, but so far my strategy of leaving it to magically fix itself has failed to yield results.  :(</p>
<p>Anyway I wanted to get a little feedback from folks.  Is anyone still using zooCST?</p>
<p>I also might split the project into two &#8211; one with old horrible MEL only tools and the other with the shiny new python tools.  The MEL tools aren&#8217;t changing anyway so its not like continually including them in releases is helpful.</p>
<p>Thoughts anyone?  Feel free to leave a comment or shoot me email.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=762</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>capturing nodes created by function</title>
		<link>http://www.macaronikazoo.com/?p=730</link>
		<comments>http://www.macaronikazoo.com/?p=730#comments</comments>
		<pubDate>Mon, 11 Jul 2011 23:20:34 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=730</guid>
		<description><![CDATA[I&#8217;ve written a few tools where being able to capture the nodes created by a particular function has come in handy. So I figured I&#8217;d blog about it in the hopes that it makes someone else&#8217;s life easier. So do what exactly? Well, the idea is to run some function, and be able to capture [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve written a few tools where being able to capture the nodes created by a particular function has come in handy.  So I figured I&#8217;d blog about it in the hopes that it makes someone else&#8217;s life easier.</p>
<p>So do what exactly?  Well, the idea is to run some function, and be able to capture all the nodes created while that function executes.  I&#8217;ll talk a bit about my motivations for this at the end of the post &#8211; for now, lets dig into the code.</p>
<p>Maya provides quite a few event based callbacks that you can hook up using the C++ API.  This hasn&#8217;t been easy to do before python came along without writing C++ code. This particular problem you could mostly solve without this API access, but not any way that would be performant.</p>
<pre class="brush:py">from maya.OpenMaya import MObjectHandle, MDGMessage, MMessage
import apiExtensions  #this module makes the maya api bindings a little easier to work with

def getNodesCreatedBy( function, *args, **kwargs ):
        '''
        returns a 2-tuple containing all the nodes created by the passed function, and
        the return value of said function
        '''

        #construct the node created callback
        newNodeHandles = []
        def newNodeCB( newNode, data ):
                newNodeHandles.append( MObjectHandle( newNode ) )

        def remNodeCB( remNode, data ):
                remNodeHandle = MObjectHandle( remNode )
                if remNodeHandle in newNodeHandles:
                        newNodeHandles.remove( remNodeHandle )

        newNodeCBMsgId = MDGMessage.addNodeAddedCallback( newNodeCB )
        remNodeCBMsgId = MDGMessage.addNodeRemovedCallback( remNodeCB )

        ret = function( *args, **kwargs )
        MMessage.removeCallback( newNodeCBMsgId )
        MMessage.removeCallback( remNodeCBMsgId )

        #NOTE: the newNodes is a list of MObjects.  If you want node names do something like this:
        #newNodes = [ str( h.object() ) for h in newNodeHandles ]
        #this solution relies on using the apiExtensions module imported above
        newNodes = [ h.object() for h in newNodeHandles ]

        return newNodes, ret</pre>
<p>So thats the code that does the magic.  Its pretty simple, even if a bit awkward to use.  Basically you pass it the function you want to run and all its corresponding args, and it will return the list of nodes created by that function, and the return value of that function.</p>
<p>Just a quick note &#8211; as it says in the comments if you don&#8217;t want MObjects returned you can easily return node names instead.  Although if you&#8217;re using the <a href="http://zootoolbox.googlecode.com/svn/trunk/apiExtensions.py" target="_blank">apiExtensions</a> module you can use MObject instances <em>as if they were node names</em>. You can grab the <a href="http://zootoolbox.googlecode.com/svn/trunk/apiExtensions.py" target="_blank">apiExtensions module from here</a>, or just grab it out of the latest release.  Or if you&#8217;re a pymel kinda guy/gal, I expect you can instantiate the PyNode class with an MObject.</p>
<p>Moving on, here is a quick example:</p>
<pre class="brush:py">from maya.cmds import *
def someFunc():
        spaceLocator()
        group( em=True )
        joint()
        polySphere()

        return 'some interesting return value'

nodesCreated, returnValue = getNodesCreatedBy( someFunc )
print nodesCreated, returnValue</pre>
<p>Thats it.  Doesn&#8217;t matter how the nodes are created, this function will capture them all.  You may also notice that the shape nodes for the locator have been included as well.  The nodesCreated list will quite literally contain ALL nodes created by the function passed in.</p>
<p>So whats it used for?</p>
<p>Just say I want to write a tool that will build a dynamics network on a given control chain.  I want to be able to turn this functionality on and off easily and I want to make sure when it gets deleted there is no cruft left behind.  First I write the code to build the network.  Then I write another function that will execute this code and capture the created nodes and put them in an object set or a container or something.</p>
<p>Skeleton Builder works in this way.  When a rig part is built the build method gets run inside this node capturing decorator and all created nodes are put into an object set.  So Skeleton Builder can easily delete a rig cleanly.  It can also more easily understand the relationship between a given node and a given part of the rig.</p>
<p>Another tool that uses this is the <a href="http://code.google.com/p/zootoolbox/source/browse/trunk/dynamicChain.py" target="_blank">Dynamics Tool</a>.  This tool will create a hair follicle setup and constrain a given list of objects to it.  It will then give you an interface to delete and create this setup on the fly, or bake the results down to keys.  The deletion of the hair setup is clean because ALL nodes are captured when the rig is created.</p>
<p>One minor thing to note &#8211; if you&#8217;re running maya 2009 there is a bug with removing node creation callbacks&#8230;  This bug is fairly harmless although it may result in noticeable slowdowns if you end up with lots of improperly removed callbacks.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=730</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>blender as an exchange format?</title>
		<link>http://www.macaronikazoo.com/?p=741</link>
		<comments>http://www.macaronikazoo.com/?p=741#comments</comments>
		<pubDate>Fri, 01 Jul 2011 18:23:28 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=741</guid>
		<description><![CDATA[Procedural-ism in a pipeline can be super useful. Of course, it depends on the project. Its more useful in projects where there are a small number of expensive, multi-faceted assets. High fidelity characters for example; with powerful facial rigs, all encompassing body deformation rigs, clothing and body part simulation etc&#8230; In almost every case, many [...]]]></description>
			<content:encoded><![CDATA[<p>Procedural-ism in a pipeline can be super useful.  Of course, it depends on the project.  Its more useful in projects where there are a small number of expensive, multi-faceted assets.  High fidelity characters for example; with powerful facial rigs, all encompassing body deformation rigs, clothing and body part simulation etc&#8230;  In almost every case, many different hands will touch this single asset and being able to express the operations to perform on the different streams of data is invaluable.</p>
<p>Lets take a clothed hero character as an example.  Breaking implementation of this character asset down into areas of expertise we&#8217;ll most likely have a character modeler, a facial modeler/rigger, a clothing sim guy and then the body articulation guy who may also be the guy who builds the puppet rig on top of the deformation rig.</p>
<p>Now obviously this could all be handled by one guy, but on most big productions it isn&#8217;t.  After all, being a fantastic facial modeler/rigger is hard.  Really hard.  And it takes many years of work, practice and study to do really well.  Similarly for simulation, modeling, deformation rigging and puppet rigging.  There are very few people in the world who are fantastic at all of these.  And even if you did give the task to one guy, it takes a long time to do all this work.  Doing the tasks in a serial fashion is usually unacceptable.  So you want to be able to split the work up, assign different people to get the job done and charge forth.</p>
<p>Making it possible to have many people work on a single asset however is kinda tricky.  How do you funnel all that data together?  Ideally you want to be able to build some sort of recipe to take a bunch of different pieces and splice them together.  But this splicing process needs to be easy to use and ideally transparent.</p>
<p>As an example lets take body articulation.  Lets build a proxy model &#8211; as close in proportions to what we think the final model will be.  Ideally this proxy model will be some early version of the model that everyone is happy with.  The articulation guy can then setup skinning and deformation rigging on this proxy geometry.  Once we start getting revisions of the final geometry we can take this proxy geometry and transfer skeletons and weights onto the final geometry and spit out a rigged version of the actual geometry.  This way the modeler and the rigger can iterate in parallel without to stepping on each other&#8217;s toes.</p>
<p>Lets take the face as another example.  In a similar way we can have the facial guy start doing the facial rig on proxy geometry.  As newer revisions of the geometry come in the facial rig can be transferred to the new geometry, tweaked as necessary and then spliced together with the rest of the body geometry.  This finalized geometry can then be combined with the articulation pass we talked about above and sent down the pipe for puppet rigging, or simulation or whatever other requirements you have.</p>
<p>The transfer of data between the proxy geometry and the finalized geometry can be done in a variety of different ways.  And which way you choose depends on the specifics of what you&#8217;re after.  Using UV space to transfer data is probably one of the most reliable methods of transfer, but you can also use closest point.</p>
<p>So how does this recipe get run?  Well there are a variety of ways of doing this, but this is what I did.  I wrote a file format to describe the recipe.  It basically was a format that recorded a bunch of operations such as LoadGeometry, MergeGeometry, CopySkinningAndSkeletonFromGeometry, ReplaceMaterial etc&#8230;  These operations required various arguments which were also recorded.  There was an editor which would allow you to setup these operations which were then saved to disk.  Then in maya I wrote a file translator plugin which would run these operations and import the resulting data.  This allowed us to reference these recipes into maya.  So for example, just say an artist wants to see the latest version of a character&#8217;s skinned geometry and facial rig.  They would simply reference in the appropriate recipe.  Then the file translator would run the latest operations on the latest data and dump the result into the maya scene.  So each time the scene is loaded the latest data is being operated on.  So artists were always seeing the latest data without having to remember to publish their files or anything like that.  The process was transparent.</p>
<p>Obviously people still need to communicate closely.  The situation is akin to multi-processing.  Some problems can easily be spread across multiple processors while others are not.  And even problems that can be split up don&#8217;t always get faster when you throw more processors at the problem.  Communication between people can be a bottleneck.  There is definitely a sweet spot, it just depends on the specifics of the asset being produced.</p>
<p>The system I worked on used a proprietary data format which worked out ok, but we ended up implementing a lot of basic functionality.  Wrap deformation, soft selection, skinning, merging of vertices etc&#8230;  It would have been a lot more powerful had I been able to use <a href="http://www.blender.org/" target="_blank">blender</a> for the data manipulation because it has all of this functionality already.</p>
<p>Anyway&#8230;  Maybe once <a href="http://www.blender.org/" target="_blank">blender</a> is available as a standalone <a href="http://www.python.org/" target="_blank">python</a> module we can get all major 3d apps to implement <a href="http://www.blender.org/" target="_blank">blender</a> exporters and a general system like this can be implemented.  <a href="http://www.blender.org/" target="_blank">Blender</a> as a 3d exchange format would be awesome.  WAY more awesome than <a href="http://www.collada.org/" target="_blank">collada</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=741</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>blender would make a GREAT python module</title>
		<link>http://www.macaronikazoo.com/?p=743</link>
		<comments>http://www.macaronikazoo.com/?p=743#comments</comments>
		<pubDate>Sun, 05 Jun 2011 01:51:31 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=743</guid>
		<description><![CDATA[Y&#8217;know what would be AWESOME?! Blender as a standalone python module. If I was able to do this: import bpy scene = bpy.import( "c:/somefile.blend" ) for obj in scene.objs: .... scene.save() Imagine how cool that would be? You&#8217;d have the worlds most powerful, scriptable 3d/2d geometry library. It&#8217;d be awesome. Incidentally, Houdini does exactly this [...]]]></description>
			<content:encoded><![CDATA[<p>Y&#8217;know what would be AWESOME?!  Blender as a standalone python module.  If I was able to do this:</p>
<pre class="brush:py">import bpy
scene = bpy.import( "c:/somefile.blend" )
for obj in scene.objs:
    ....
scene.save()</pre>
<p>Imagine how cool that would be?  You&#8217;d have the worlds most powerful, scriptable 3d/2d geometry library.  It&#8217;d be awesome.</p>
<p>Incidentally, Houdini does exactly this as far as I understand.  I don&#8217;t know much about houdini (apart from how unbelievably cool it looks) but I watched a video on this where they imported the houdini module from within maya, and then did a whole bunch of cool stuff and blatted it into maya.  You could basically leverage a huge amount of houdini functionality in any app that runs python.  It also means you can do a whole lot of stuff without the horrid cost of loading the UI.</p>
<p>As cool as it is that houdini can do this, it would be infinitely cooler if blender could do this.</p>
<p>How &#8217;bout it blender devs?  I think this feature would make blender amazingly useful to the games/film community.  I&#8217;ll go into this a bit more in a future post.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=743</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>A more thorough Interface class</title>
		<link>http://www.macaronikazoo.com/?p=726</link>
		<comments>http://www.macaronikazoo.com/?p=726#comments</comments>
		<pubDate>Fri, 20 May 2011 05:16:49 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=726</guid>
		<description><![CDATA[I did a bit more messing about this evening with the interface class idea. I think the below implementation is fairly complete. It handles multiple inheritance cases where the interface gets satisfied by one of the parent classes. def interfaceTypeFactory( metaclassSuper=type ): ''' returns an "Interface" metaclass. Interface classes work as you'd expect. Every method [...]]]></description>
			<content:encoded><![CDATA[<p>I did a bit more messing about this evening with the interface class idea.  I think the below implementation is fairly complete.  It handles multiple inheritance cases where the interface gets satisfied by one of the parent classes.</p>
<pre class="brush:py">def interfaceTypeFactory( metaclassSuper=type ):
	'''
	returns an "Interface" metaclass.  Interface classes work as you'd expect.  Every method implemented
	on the interface class must be implemented on subclasses otherwise a TypeError will be raised at
	class creation time.

	usage:
		class IFoo( metaclass=interfaceTypeFactory() ):
			def bar( self ): pass

		subclasses must implement the bar method

	NOTE: the metaclass that is returned inherits from the metaclassSuper arg, which defaults to type.  So
	if you want to mix together metaclasses, you can inherit from a subclass of type.  For example:
		class IFoo( metaclass=interfaceTypeFactory( trackableTypeFactory() ) ):
			def bar( self ): pass

		class Foo(IFoo):
			def bar( self ): return None

		print( IFoo.GetSubclasses() )
	'''
	class _AbstractType(metaclassSuper):
		_METHODS_TO_IMPLEMENT = None
		_INTERFACE_CLASS = None

		def _(): pass
		_FUNC_TYPE = type( _ )

		def __new__( cls, name, bases, attrs ):
			newCls = metaclassSuper.__new__( cls, name, bases, attrs )

			#if this hasn't been defined, then cls must be the interface class
			if cls._METHODS_TO_IMPLEMENT is None:
				cls._METHODS_TO_IMPLEMENT = methodsToImplement = []
				cls._INTERFACE_CLASS = newCls
				for name, obj in attrs.items():
					if type( obj ) is cls._FUNC_TYPE:
						methodsToImplement.append( name )

			#otherwise it is a subclass that should be implementing the interface
			else:
				if cls._INTERFACE_CLASS in bases:
					for methodName in cls._METHODS_TO_IMPLEMENT:

						#if the newCls' methodName attribute is the same method as the interface
						#method, then the method hasn't been implemented.  Its done this way because
						#the newCls may be inheriting from multiple classes, one of which satisfies
						#the interface - so we can't just look up the methodName in the attrs dict
						if getattr( newCls, methodName, None ).im_func is getattr( cls._INTERFACE_CLASS, methodName ).im_func:
							raise TypeError( "The class %s doesn't implement the required method %s!" % (name, methodName) )

			return newCls

	return _AbstractType

class ITest( metaclass=interfaceTypeFactory() ):
	def something( self ): pass
	def otherthing( self ): pass

class Test_implementsAll(ITest):
	def something( self ): pass
	def otherthing( self ): pass

class Test_subclassImplementsAll(Test_implementsAll):
	pass

class SimilarInterface(object):
	def something( self ): pass
	def otherthing( self ): pass

class MultipleInheritanceTest(SimilarInterface, ITest):
	pass

#will throw TypeError
class Test_implementsSome(ITest):
	def something( self ): pass

#will throw TypeError
class Test_implementsNone(ITest):
	pass</pre>
<p>There are some simple tests at the bottom that demonstrate the idea.</p>
<p>As you can see, the class MultipleInheritanceTest has no methods, but its parent class SimilarInterface does implement the required methods, so the class passes without issue.</p>
<p>Anyway &#8211; I figured after the previous post I should at least finish the thought.  The implementation in the last post wasn&#8217;t complete.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=726</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Interface classes in python</title>
		<link>http://www.macaronikazoo.com/?p=721</link>
		<comments>http://www.macaronikazoo.com/?p=721#comments</comments>
		<pubDate>Thu, 19 May 2011 05:41:07 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=721</guid>
		<description><![CDATA[Python doesn&#8217;t really provide a defined way to implement interfaces. Python 2.6 provides abstract classes &#8211; although what I don&#8217;t like about this implementation is that you don&#8217;t get an exception until you try to instantiate the broken subclass, by which time it might be too late. At least, thats what it looks like from [...]]]></description>
			<content:encoded><![CDATA[<p>Python doesn&#8217;t really provide a defined way to implement interfaces.  Python 2.6 provides abstract classes &#8211; although what I don&#8217;t like about this implementation is that you don&#8217;t get an exception until you try to instantiate the broken subclass, by which time it might be too late.  At least, thats what it looks like from what I&#8217;ve read (for the most part I&#8217;m still stuck using python 2.5 &#8211; as I mainly work in maya 2009 so I haven&#8217;t bothered messing with any of this yet).  I want a solution that happens at parse time &#8211; the equivalent to compile type for statically typed languages.</p>
<p>So I toyed around a bit this evening and came up with what you see below.  It seems to work &#8211; at least for this trivial case.  I&#8217;m not sure this is necessarily a good implementation, but it goes to show that its reasonably easy to do.</p>
<pre class="brush:py">def interfaceFactory( metaclassSuper=type ):
	class AbstractClass(metaclassSuper):
		_METHODS_TO_IMPLEMENT = None

		def __new__( cls, name, bases, attrs ):
			subCls = metaclassSuper.__new__( cls, name, bases, attrs )

			#if this hasn't been defined, then cls must be the interface class
			if cls._METHODS_TO_IMPLEMENT is None:
				def _(): pass
				funcType = type( _ )
				cls._METHODS_TO_IMPLEMENT = methodsToImplement = []
				for name, obj in attrs.items():
					if type( obj ) is funcType:
						methodsToImplement.append( name )

			#otherwise it is a subclass that should be implementing the interface
			else:
				for methodName in cls._METHODS_TO_IMPLEMENT:
					if methodName not in attrs:
						raise TypeError( "The subclass %s doesn't implement the %s attribute!" % (name, methodName) )

			return subCls

	return AbstractClass

class TrackableType(type):
	_SUBCLASSES = []

	def __new__( cls, name, bases, attrs ):
		newCls = type.__new__( cls, name, bases, attrs )
		cls._SUBCLASSES.append( newCls )

		return newCls

class ISomething( metaclass=interfaceFactory( TrackableType ) ):
	def something( self ): pass
	def otherthing( self ): pass

class Something(ISomething):
	def something( self ): pass</pre>
<p>If you run this code you&#8217;ll see a TypeError is raised complaining that the Something class doesn&#8217;t implement the otherthing method as soon as you import the script.  Not when you try to instantiate a Something instance.</p>
<p>You&#8217;ll notice it only cares about methods being implemented &#8211; so if you wanted to force implementation of various properties in inherited classes, you&#8217;d probably need a few more conditionals in there &#8211; but I generally try to avoid properties.  They feel dirty.</p>
<p>Anyway, nothing terribly useful, but I thought others might find it interesting.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=721</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Python Objects In A TextScrollList</title>
		<link>http://www.macaronikazoo.com/?p=707</link>
		<comments>http://www.macaronikazoo.com/?p=707#comments</comments>
		<pubDate>Sun, 15 May 2011 05:09:48 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=707</guid>
		<description><![CDATA[One of the more useful classes in baseMelUI is the MelObjectScrollList.  Its simplified UI code enough times for me that I figured it&#8217;d be worthwhile pointing it out to others. The MelObjectScrollList class is subclass of MelTextScrollList &#8211; which is a wrapper to the cmds.textScrollList widget command.  The class provides a neat object oriented way [...]]]></description>
			<content:encoded><![CDATA[<p>One of the more useful classes in baseMelUI is the MelObjectScrollList.  Its simplified UI code enough times for me that I figured it&#8217;d be worthwhile pointing it out to others.</p>
<p>The MelObjectScrollList class is subclass of MelTextScrollList &#8211; which is a wrapper to the cmds.textScrollList widget command.  The class provides a neat object oriented way to interact with the scroll list.  It hides some of the weirdness of the command (such as indices starting at 1 &#8211; WTF?!) and makes it possible to write object oriented code with all the good stuff you expect.</p>
<p>Back to the MelObjectScrollList &#8211; so the idea with this class is to use the textScrollList widget to display lists of data of any kind.  Want a list of dictionaries?  No problem.  Want a list of MObjects?  Vectors?  You get the idea.  You can pass any python object you want into the UI and it will display them in the widget, let users select them, and then return you the list of objects that the user selected.  Its very useful.</p>
<p>So how does it work?</p>
<p>Well, the base class contains most of the functionality you need.  There are two general cases.  If you want to display lists of objects that already have a reasonably sensible, user friendly string representation, then you can use the MelObjectScrollList directly:</p>
<pre class="brush:py">import maya.cmds as cmds
from baseMelUI import MelObjectScrollList

w = cmds.window( 'test', t='test', h=300, w=300 )
c = cmds.columnLayout()
objScrollList = MelObjectScrollList( c, h=250 )
objScrollList.setItems( [ [], {}, ['some', 'list', 'of', 'strings'], dict, MelObjectScrollList ] )
cmds.showWindow( w )

def onSel( *a ):
	print objScrollList.getSelectedItems()

objScrollList.setChangeCB( onSel )</pre>
<p>Incidentally I don&#8217;t normally write UI code like this &#8211; I just figured doing it this way might make it more available to folks who don&#8217;t use baseMelUI for their UI creation.  Its also a good demonstration of how the library can be used in a mix and match way (even with pymel!).</p>
<p>So as you can see if you run this code (and have the zooToolbox &#8211; or at the very least, <a href="http://zootoolbox.googlecode.com/svn/trunk/baseMelUI.py" target="_blank">the baseMelUI module</a> in your python path) you&#8217;ll see a window pop up with the above objects in it.  They look just as if you&#8217;d called print on each one.</p>
<p>Now if you want to customize the way these objects are displayed in the UI, then you simply need to subclass the MelObjectScrollList and override the itemToStr method.  Here is an example:</p>
<pre class="brush:py">from filesystem import Path
from baseMelUI import MelVLayout, MelObjectScrollList, BaseMelWindow

class FilenameScrollList(MelObjectScrollList):
	def itemAsStr( self, item ):
		return Path( item )[-1]

class Test(BaseMelWindow):
	WINDOW_NAME = 'test'

	def __init__( self ):
		f = MelVLayout( self )
		self.fileListUI = FilenameScrollList( f )
		self.fileListUI.setItems( Path( "c:/windows" ).files() )
		self.fileListUI.setChangeCB( self.on_itemSelect )
		f.expand = True
		f.layout()
		self.show()
	def on_itemSelect( self, *a ):
		print self.fileListUI.getSelectedItems()

Test()</pre>
<p>In this example you should see a window open up with a list of the file names in c:\windows.  When you select an entry in the list, you&#8217;ll see the full path to that file printed in the script editor.  The FilenameScrollList keeps track of the objects you put into it.  It acts as a broker between the data and the UI.  So using it simplifies code a heap.</p>
<p>Another neat feature thats built into the base class is view filtering.  So building on the above code you can do things like this:</p>
<pre class="brush:py">ui = Test()
ui.fileListUI.setFilter( '.exe' )</pre>
<p>As you can see, once you run the setFilter() method the list now only displays files containing &#8220;.exe&#8221;.  The UI still retains a list of all the files, but it only displays the items that pass the filter.</p>
<p>Lastly the other useful feature the class provides is re-ordering.  The methods are &#8220;moveSelectedItemsUp()&#8221; and &#8220;moveSelectedItemsDown()&#8221;.  You can easily hook up these methods to buttons, popup menus or whatever else you want.</p>
<pre class="brush:py">from filesystem import Path
from baseMelUI import BaseMelWindow, MelHSingleStretchLayout, MelVLayout, MelObjectScrollList, MelButton

class FilenameScrollList(MelObjectScrollList):
	ALLOW_MULTI_SELECTION = True

	def itemAsStr( self, item ):
		return Path( item )[-1]

class Test(BaseMelWindow):
	WINDOW_NAME = 'test'

	def __init__( self ):
		f = MelHSingleStretchLayout( self )
		self.fileListUI = FilenameScrollList( f )
		self.fileListUI.setItems( Path( "c:/windows" ).files() )
		self.fileListUI.setChangeCB( self.on_itemSelect )
		f.setStretchWidget( self.fileListUI )

		v = MelVLayout( self )
		MelButton( v, l='up', c=self.on_up )
		MelButton( v, l='down', c=self.on_down )
		v.expand = True
		v.layout()

		f.expand = True
		f.layout()
		self.show()
	def on_itemSelect( self, *a ):
		print self.fileListUI.getSelectedItems()
	def on_up( self, *a ):
		self.fileListUI.moveSelectedItemsUp()
	def on_down( self, *a ):
		self.fileListUI.moveSelectedItemsDown()

Test()</pre>
<p>NOTE: make sure to grab the latest <a href="http://zootoolbox.googlecode.com/svn/trunk/baseMelUI.py" target="_blank">baseMelUI.py from here</a>.  While writing this post I fixed a bug with the moveSelectedItems methods when a filter is set.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=707</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>simple skin weights tool</title>
		<link>http://www.macaronikazoo.com/?p=673</link>
		<comments>http://www.macaronikazoo.com/?p=673#comments</comments>
		<pubDate>Wed, 11 May 2011 05:04:36 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=673</guid>
		<description><![CDATA[Referencing. What a pain in the butt it can be. One of the big pains can be when you&#8217;re setting up a character. Skinning happens in one file, a rig in another, and animation in a third file. At least, thats how I&#8217;ve generally done it. There are variations on this theme, but the basic [...]]]></description>
			<content:encoded><![CDATA[<p>Referencing.  What a pain in the butt it can be.  One of the big pains can be when you&#8217;re setting up a character.  Skinning happens in one file, a rig in another, and animation in a third file.  At least, thats how I&#8217;ve generally done it.  There are variations on this theme, but the basic idea of model and rig in one file being referenced into an animated file is a pretty common paradigm I imagine.</p>
<p>So a lot of the time I find I&#8217;ll author my skeleton, skin the geo and build the rig only to find that the skinning doesn&#8217;t hold up so well as soon as I strike a certain pose in the animation.</p>
<p>Some people solve this by having a &#8220;test suite&#8221; of calisthenics they put their characters through before they get animated.  And sure, this can work well provided you have some way of describing these calisthenics in a generalized way that will apply to any and all characters that come down the pipe.  A spider is going to need a different set of poses to a human, which will be different again for a multi-headed dragon which is different again from a snake like creature with 6 arms.</p>
<p>Anyway, this happens enough that I figured I&#8217;d try to alleviate the problem a bit.  So I wrote a tool that will take skin weight edits made in the animation file, and push them back to the file the model geometry is stored in.</p>
<p>The process goes something like this.</p>
<p>You&#8217;re animating away, and you strike a pose only to realize you&#8217;ve got horrible shearing happening somewhere.  So in your animation scene, with the pose that breaks the skinning you crack open whatever weight/editing painting tool you&#8217;re into and go about fixing the problem area.  Now because you&#8217;re in an animation scene the skinCluster you&#8217;re editing actually lives in a completely different file.</p>
<p>So you run this tool.  The tool grabs the skinCluster edits made in the animation scene and shoves em into memory.  Then it removes the ref edits from the skinCluster node and saves the animation scene.  Then it opens up the file the skinCluster node lives in and applies the fixed weighting it stored from the animation scene and saves the file.  Finally it opens the scene you were originally in and voila.  Just like that, you&#8217;re weights are fixed across the board.</p>
<p>Its not the worlds most useful tool &#8211; but it saves the occasional headache, and in fact gets used quite regularly.  It makes it easy to edit/fix skin weights &#8220;in context&#8221; without any convoluted workflow.  And in fact having such a tool means minor skin weight polishing happens more frequently now than before the tool existed because its so easy to do.  Skin fixes can be done in any animation as soon as the problem is seen.</p>
<p>NOTE: maya supposedly has functionality to &#8220;Save Reference Edits&#8221; but as usual, it seems like a largely useless feature.  Has anyone managed to use it successfully?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=673</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>useful-ifying error messages</title>
		<link>http://www.macaronikazoo.com/?p=678</link>
		<comments>http://www.macaronikazoo.com/?p=678#comments</comments>
		<pubDate>Thu, 05 May 2011 03:37:13 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=678</guid>
		<description><![CDATA[Printing out warning and error messages from script is the easiest way to communicate things to a user. Maya has generally trained users reasonably well to look for warnings and errors in the script editor. This isn&#8217;t really ideal mind you &#8211; but thats a topic for another time. Anyway, as useful as warnings and [...]]]></description>
			<content:encoded><![CDATA[<p>Printing out warning and error messages from script is the easiest way to communicate things to a user.  Maya has generally trained users reasonably well to look for warnings and errors in the script editor.  This isn&#8217;t really ideal mind you &#8211; but thats a topic for another time.</p>
<p>Anyway, as useful as warnings and errors are, if you&#8217;re writing modular, re-usable code then writing stuff to the script editor isn&#8217;t always as helpful as it should be.  Sometimes its hard to know exactly where a message is coming from.  Sure, you can grep your code base, but sometimes even this isn&#8217;t that easy to do if the message itself is constructed.</p>
<p>In the olden days of MEL I would often try to put the function name at the beginning of the message in the hope of making it easier to track down the reference at a later date &#8211; but this wasn&#8217;t terribly ideal either.  It required me to remember to do it, plus it was just one more reference to update when refactoring.</p>
<p>Python however gives you the tools to solve this problem in a nifty way.  You can write a print handler that will figure out where in the code the print/warning/error statement happened and put all that in the message.</p>
<p>For example, I can write code like this:</p>
<pre class="brush:py">printWarningStr( "WOW!" )</pre>
<p>And the following message gets spewed to the console:</p>
<pre># Warning: WOW!: from line 14 in the function main in the script D:\someScript.py #</pre>
<p>As you can see, in the message is encoded the warning prefix (it also turns the script line pink as per usual warning messages in maya) as well as the name of the function it came from, and the location of the script.  You could also put the line number in there as well, but I figured I&#8217;d omit that for the sake of brevity.</p>
<p>Python is such an awesome language &#8211; so deliciously introspective.</p>
<p>So does this get done?</p>
<p>Well, I have two places that I setup this code.  The first is in a generic, maya agnostic module so that I can use it in non-maya tools.  It looks like this:</p>
<pre class="brush:py">def generateTraceableStrFactory( prefix, printFunc=None ):
	'''
	returns 2 functions - the first will generate a traceable message string, while
	the second will print the generated message string.  The second is really a
	convenience function, but is called enough to be worth it

	you can also specify your own print function - if no print function is specified
	then the print builtin is used
	'''
	def generateTraceableStr( *args, **kw ):
		frameInfos = inspect.getouterframes( inspect.currentframe() )

		_nFrame = kw.get( '_nFrame', 1 )

		#frameInfos[0] contains the current frame and associated calling data, while frameInfos[1] is the frame that called this one - which is the frame we want to print data about
		callingFrame, callingScript, callingLine, callingName, _a, _b = frameInfos[_nFrame]
		lineStr = 'from line %s in the function %s in the script %s' % (callingLine, callingName, callingScript)

		return '%s%s: %s' % (prefix, ' '.join( map( str, args ) ), lineStr)

	def printTraceableStr( *args ):
		msg = generateTraceableStr( _nFrame=2, *args )
		if printFunc is None:
			print( msg )
		else:
			printFunc( msg )

	return generateTraceableStr, printTraceableStr

generateInfoStr, printInfoStr = generateTraceableStrFactory( '*** INFO ***: ' )
generateWarningStr, printWarningStr = generateTraceableStrFactory( '*** WARNING ***: ' )
generateErrorStr, printErrorStr = generateTraceableStrFactory( '*** ERROR ***: ' )
</pre>
<p>As you can see, this is a factory function that returns a function that will generate the message, and one that will actually print the message.  You almost always want to use the print one, but if you ever need to generate an info/warning/error message using the same code, the generate function is available.</p>
<p>So the factory function allows you specify a print handler.  It defaults to python&#8217;s built in print function (or statement if you&#8217;re pre 3.0), but this makes it easy to generate maya specific print functions using MGlobal.displayWarning and MGlobal.displayError.  In another module I have the following code:</p>
<pre class="brush:py">from maya.OpenMaya import MGlobal

generateInfoStr, printInfoStr = generateTraceableStrFactory( '*** INFO ***', MGlobal.displayInfo )
generateWarningStr, printWarningStr = generateTraceableStrFactory( '', MGlobal.displayWarning )
generateErrorStr, printErrorStr = generateTraceableStrFactory( '', MGlobal.displayError )</pre>
<p>So in maya tools I simply use the above functions instead of print and voila &#8211; I get useful information in the console spew.</p>
<p>Anyway &#8211; not a super useful chunk of code, but I thought others might find such a technique useful.  Its ~15 lines of code that occasionally saves me a grep of the codebase.  Plus its good practice to have logging hooks like this in place.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=678</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>beware!</title>
		<link>http://www.macaronikazoo.com/?p=668</link>
		<comments>http://www.macaronikazoo.com/?p=668#comments</comments>
		<pubDate>Tue, 26 Apr 2011 04:33:34 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=668</guid>
		<description><![CDATA[Sometimes I wish I could go back in time and slap some sense into myself. One of the very first things I discovered when I was learning python was operator overloading. I&#8217;d not used a language with such power before and stupidly I put this newfound knowledge to use in all sorts of crazy ways. [...]]]></description>
			<content:encoded><![CDATA[<p>Sometimes I wish I could go back in time and slap some sense into myself.  One of the very first things I discovered when I was learning python was operator overloading.  I&#8217;d not used a language with such power before and stupidly I put this newfound knowledge to use in all sorts of crazy ways.</p>
<p>Today I found an early piece of code I wrote.  Its a useful chunk of code &#8211; parses config files.</p>
<p>Anyway I wanted to add some functionality to the code.  See the config files it parses are often written by hand, and the format supports C-style comments.  Currently the parser throws away the comments which is no drama if you just need read support &#8211; but a feature request came up that would require the writing of these files from a tool.  So I need to preserve comments as best as I can.</p>
<p>Not a big drama to add the functionality except that part of the code I ended up having to touch involved one of the operator overloads &#8211; in this case the [] operator (__getitem__ in python).  The existing overload was done in a super weird way (what I wanted to slap &#8220;past me&#8221; for).  I made it so you could do this:</p>
<pre class="brush:py">f = ConfigFile( 'd:/something.txt' )
f.read()
f[0, 2, -1, 9]</pre>
<p>Which would navigate the config file document hierarchy.  Why?!  Well, probably because I was a n00b and figured it&#8217;d be &#8220;cool&#8221;.  SLAP!</p>
<p>Anyway, whats the point here?  Well, I guess what I&#8217;m trying to say is (apart from don&#8217;t write idiotic code) &#8211; if you&#8217;re going to overload operators be really really careful.  Tracking down where the code is used is hard.  You can&#8217;t really grep your codebase for anything in particular &#8211; after all, the whole point of operator overloading is so the interface to the action looks like a standard language operator.  In this particular case it wasn&#8217;t a big deal &#8211; the code is used in only a few places.  But if this code had wider use, it would&#8217;ve been a nightmare.</p>
<p>This is pretty obvious to anyone who has been coding for awhile &#8211; but after this recent reminder I figured a little PSA might save others some pain</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=668</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>more on being back</title>
		<link>http://www.macaronikazoo.com/?p=655</link>
		<comments>http://www.macaronikazoo.com/?p=655#comments</comments>
		<pubDate>Thu, 14 Apr 2011 15:55:52 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=655</guid>
		<description><![CDATA[After this post, Brad Clark from Rigging Dojo asked me for more info.  Not sure if I can link to a tweet, but the question was this: just wondering what you are feeling are sticking points, what area are you having to work hardest on now v. back in the flow I thought the answer [...]]]></description>
			<content:encoded><![CDATA[<p>After <a title="back in the saddle" href="http://www.macaronikazoo.com/?p=616">this post</a>, Brad Clark from <a href="http://www.riggingdojo.com/" target="_blank">Rigging Dojo</a> asked me for more info.  Not sure if I can link to a tweet, but the question was this:</p>
<pre>just wondering what you are feeling are sticking points, what
area are you having to work hardest on now v. back in the flow</pre>
<p>I thought the answer was worth more than a tweet, so here we are.  A quick bit of background to set the stage:  I started out in 3d as an animator.  I morphed into the role of a tech artist because those around me didn&#8217;t step up &#8211; technical leadership is important, and I guess it suited me.  I&#8217;ve discussed it <a title="Skeleton Builder 101: part 1" href="http://www.macaronikazoo.com/?p=530">here before</a> (third paragraph) so I won&#8217;t bore you again.  But basically thats been my path.  I started out doing lots of animation, and have ended up doing not so much.  In fact, I have done very little animation during the last almost 5 years now.</p>
<p>For now the animation work I&#8217;m doing is very much a part time thing.  There are still loads of technical problems and improvements to be made, and honestly, I&#8217;m a much better technical artist than I am an animator.  But doing animation is something that I think is really important for me as a technical animator.  Using the tools I&#8217;ve written, working through the workflows I&#8217;ve helped define, having to deal with all the bugs, shortcomings etc of the work environment I&#8217;ve helped create puts me in the shoes of my users and forces me to see and understand the implications of my design decisions.  And I think thats really important.</p>
<p>Anyway &#8211; sticking points.  Well, the first would be the calibre of the folks around me.  ;)  Yeah, they&#8217;re all kinda good.  I mean, I was never the best animator, but the animators I work with literally are, some of the best.  I guess this isn&#8217;t really a sticking point &#8211; its more of an opportunity for me to learn and grow.</p>
<p>But the other big thing is the fact that its hard to stop thinking about the technical side of things while I&#8217;m animating.  Its been such a part of my mental process for so long that its really hard to turn it off.  And I think this is the biggest thing.  So many parts of the animating process have the potential to be improved, sped up, optimized, made easier etc&#8230;  And when I&#8217;m animating, all these thoughts are running through my head.  So just pushing those thoughts aside &#8211; or at least shelving them for later &#8211; is hard.</p>
<p>Like any creative process you do your best work when you&#8217;re in &#8220;the zone&#8221;.  And getting into &#8220;the zone&#8221; is all about focusing all your thoughts on what you&#8217;re doing.  Whether that be animating, painting, composing music or writing code &#8211; if you can push all thoughts aside except for those related to what you&#8217;re doing, you can usually get into this amazing mental groove.  Its awesome.  So I find it very hard to get in &#8220;the zone&#8221; when I&#8217;m animating these days.  It doesn&#8217;t stop me from doing work, but it does make it harder and less efficient.</p>
<p>Anyway, Brad &#8211; and anyone else who was interested, I hope that answers your question!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=655</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Drag and Drop Callbacks</title>
		<link>http://www.macaronikazoo.com/?p=632</link>
		<comments>http://www.macaronikazoo.com/?p=632#comments</comments>
		<pubDate>Wed, 13 Apr 2011 17:40:45 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=632</guid>
		<description><![CDATA[Building UI in maya is usually a fairly horrible task.  I have my own UI wrappers which make building maya less painful.  It actually makes it very similar to writing UI in toolkits like wxpython or pyqt &#8211; obviously without the ability to control painting.  And it also makes encapsulating UI really easy &#8211; which [...]]]></description>
			<content:encoded><![CDATA[<p>Building UI in maya is usually a fairly horrible task.  I have my own UI wrappers which make building maya less painful.  It actually makes it very similar to writing UI in toolkits like wxpython or pyqt &#8211; obviously without the ability to control painting.  And it also makes encapsulating UI really easy &#8211; which is a big deal.  Anyway if you&#8217;re interested there is a <a title="Maya UI Classes" href="http://www.macaronikazoo.com/?page_id=311">post here</a> that goes into a bit more detail &#8211; or you can check out the code for something like the <a href="http://code.google.com/p/zootoolbox/source/browse/trunk/skeletonBuilderUI.py" target="_blank">Skeleton Builder UI</a>, or the up-coming <a href="http://code.google.com/p/zootoolbox/source/browse/trunk/picker.py" target="_blank">picker tool</a>.</p>
<p>Which is actually where the drag/drop functionality gets used.  So in this tool there is a canvas on which you can place &#8220;buttons&#8221; and arrange them in various ways.  These buttons can have somewhat arbitrary functionality, but by default they simply select objects.  The idea being that you can arrange them in the shape of your character such that picking the appropriate rig control is easy and intuitive (you&#8217;ve probably seen them before).  In viewport controls are great, but they can make the silhouette harder to read, and they&#8217;re sometimes hard to find which causes the animator to do more camera manipulation than is desirable.  Of course having to move the mouse/focus over to a picker tool isn&#8217;t ideal either but it definitely has its time and place in the toolset.</p>
<p>But again I digress.  So part of making the tool reasonably easy to use is to make it easy to arrange these buttons &#8211; and the easiest way to do that is to use a drag and drop paradigm.  Well, I guess technically the easiest way is to make it auto-magic, but that is a topic for a future post (and is an up-coming skeleton builder feature!  ;).</p>
<p>Anyway &#8211; so drag and drop.  I&#8217;ve never bothered with drag and drop in maya before.  Looking at the docs I figued it&#8217;d be straight forward enough.  And in hindsight it actually is straight forward &#8211; although when I was prototyping I was blocked by a horrible bug/shortcoming.</p>
<p>Currently I&#8217;m laden with maya 2009 &#8211; and the following doesn&#8217;t seem to apply to 2011 (not sure about 2010).  But in 2009 if you set a drop callback that returns nothing, or an empty list &#8211; the drop callback silently fails.  Thats right &#8211; no warning, no error, no exception, just silent failure.  In fact, for awhile there I figured perhaps drop callbacks hadn&#8217;t been implemented in 2009 for python.  It wouldn&#8217;t be the first widget to be incompletely exposed via python &#8211; look at the <a href="http://download.autodesk.com/us/maya/2011help/CommandsPython/treeView.html#flagpressCommand" target="_blank">press command</a> for the treeView widget (incidentally the tree widget wrapping in <a href="http://code.google.com/p/zootoolbox/source/browse/trunk/baseMelUI.py" target="_blank">baseMelUI</a> has a <a href="http://code.google.com/p/zootoolbox/source/browse/trunk/baseMelUI.py#1736" target="_blank">nice workaround</a> for this)&#8230;</p>
<p>Just to be explicit &#8211; here is an example that fails:</p>
<pre class="brush:py">def dropCB( * ): return []</pre>
<p>By changing the drop callback to return [""] makes everything work as expected.</p>
<p>Anyway &#8211; hopefully this little nugget saves someone else a few tufts of hair.  Of course, after discovering this <a href="http://xyz2.net/mel/mel.107.htm" target="_blank">I found this page</a> &#8211; which, while not talking python here would probably have put me back on track.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=632</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>back in the saddle</title>
		<link>http://www.macaronikazoo.com/?p=616</link>
		<comments>http://www.macaronikazoo.com/?p=616#comments</comments>
		<pubDate>Wed, 13 Apr 2011 17:36:35 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=616</guid>
		<description><![CDATA[Its been awhile since I&#8217;ve done much in maya. I mean I&#8217;ve done bits and pieces here and there, but actual full time type work &#8211; its been awhile.  I&#8217;ve spent most of the last year doing a bunch of pipeline work only tangentially related to maya.  It was kinda fun and a nice challenge [...]]]></description>
			<content:encoded><![CDATA[<p>Its been awhile since I&#8217;ve done much in maya.  I mean I&#8217;ve done bits and pieces here and there, but actual full time type work &#8211; its been awhile.  I&#8217;ve spent most of the last year doing a bunch of pipeline work only tangentially related to maya.  It was kinda fun and a nice challenge too, but its great to be back doing some actual production/tools work again.</p>
<p>I&#8217;ve even been animating!  Man, its been a long time since I&#8217;ve done that.  It shows too &#8211; it&#8217;ll take me awhile to get back to where I was.  But thankfully I work with some amazing animators who will whip me into shape.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=616</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Live event at the Dojo</title>
		<link>http://www.macaronikazoo.com/?p=649</link>
		<comments>http://www.macaronikazoo.com/?p=649#comments</comments>
		<pubDate>Sat, 09 Apr 2011 06:03:17 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=649</guid>
		<description><![CDATA[The guys at Rigging Dojo are putting on a live event with Jason Parks. Check out the details here.  I didn&#8217;t go to GDC, but I did see the notes/videos from Jason&#8217;s GDC talk.  He shows off some sweet eclipse features &#8211; I haven&#8217;t tried eclipse with python but it looks like it has a [...]]]></description>
			<content:encoded><![CDATA[<p>The guys at <a href="http://www.riggingdojo.com/" target="_blank">Rigging Dojo</a> are putting on a live event with Jason Parks.  Check out the <a href="http://tech-artists.org/forum/showthread.php?t=1586" target="_blank">details here</a>.  I didn&#8217;t go to GDC, but I did see the <a href="http://www.jason-parks.com/artoftech/?p=139" target="_blank">notes/videos</a> from Jason&#8217;s GDC talk.  He shows off some sweet eclipse features &#8211; I haven&#8217;t tried eclipse with python but it looks like it has a few features that <a href="http://www.wingide.com/" target="_blank">wing</a> doesn&#8217;t have (or maybe I just haven&#8217;t found them yet <img src='http://www.macaronikazoo.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  ).</p>
<p>Anyway, it sounds interesting so you should definitely check it out!  If you&#8217;re not using an IDE to write tools with &#8211; you should definitely check this out.  For the longest time I wrote scripts in a text editor (I didn&#8217;t have much choice &#8211; MEL has no IDE) and when I started using <a href="http://www.wingide.com/" target="_blank">wing</a> a few years ago &#8211; holy cow!  Being able to set breakpoints and step through your code as it executes &#8211; and experiment with code inside a live, running script was a HUGE productivity booster.  I&#8217;ve not used a print statement for debugging in a long time.</p>
<p>Props to the <a href="http://www.riggingdojo.com/" target="_blank">Rigging Dojo</a> guys for putting together great events for the community like this!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=649</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>New comment system</title>
		<link>http://www.macaronikazoo.com/?p=644</link>
		<comments>http://www.macaronikazoo.com/?p=644#comments</comments>
		<pubDate>Thu, 31 Mar 2011 07:04:28 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=644</guid>
		<description><![CDATA[I&#8217;m trying out the Disqus comment system &#8211; I&#8217;m not quite sure whether it will be better or worse, but it&#8217;ll hopefully make it easier to leave comments without having to create an account on my site (it always annoys me when I have to create yet another account). Either way, hopefully it&#8217;ll make commenting [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m trying out the Disqus comment system &#8211; I&#8217;m not quite sure whether it will be better or worse, but it&#8217;ll hopefully make it easier to leave comments without having to create an account on my site (it always annoys me when I have to create yet another account).  Either way, hopefully it&#8217;ll make commenting and conversations around posts easier.</p>
<p>You can post comments using an existing google, twitter or facebook account so you no longer have to remember your account details specific to this site.</p>
<p>Anyway &#8211; please leave comments or send mail if you have an opinion either way!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=644</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Stupid Node Name Paths</title>
		<link>http://www.macaronikazoo.com/?p=630</link>
		<comments>http://www.macaronikazoo.com/?p=630#comments</comments>
		<pubDate>Wed, 23 Mar 2011 23:06:53 +0000</pubDate>
		<dc:creator>hamish</dc:creator>
				<category><![CDATA[main]]></category>

		<guid isPermaLink="false">http://www.macaronikazoo.com/?p=630</guid>
		<description><![CDATA[I remember when every node name in maya had to be unique.  Made dealing with nodes just using their name really easy.  Then alias introduced name paths so you could have two nodes with the same leaf name as long as the node still had a unique path.  Its been a pain ever since.  Not [...]]]></description>
			<content:encoded><![CDATA[<p>I remember when every node name in maya had to be unique.  Made dealing with nodes just using their name really easy.  Then alias introduced name paths so you could have two nodes with the same leaf name as long as the node still had a unique path.  Its been a pain ever since.  Not a big pain mind you &#8211; just an annoying ache that keeps coming back over and over again.</p>
<p>Anyway I had an annoying bug with one of my tools the other day.  Basically I was passing in a node &#8211; lets say it was called &#8220;<strong>someNode</strong>&#8220;.  Anyway at the start of the function call it was totally valid, but by the end of the function it didn&#8217;t exist.  There were a couple of problems that teamed up to make it extra annoying.  The first is that objExists will return True if any node with the given <em>leaf name</em> exists.  So by the end of the function &#8211; objExists( &#8220;<strong>someNode</strong>&#8221; ) was returning True even though any other command would throw a RuntimeError.</p>
<p>As it turns out the function was creating a new node with the same leaf name.  So as soon as the new node was created, the name of &#8220;<strong>someNode</strong>&#8221; immediately changed to &#8220;<strong>|someNode</strong>&#8220;.</p>
<p>Now I know what you&#8217;re thinking &#8211; &#8220;<em>Dude if you&#8217;d just use pymel you wouldn&#8217;t have had this drama!</em>&#8220;.  And you&#8217;re totally right.  If thats the way you roll, good for you.  But I expect you know my <a title="more thoughts on pymel" href="http://www.macaronikazoo.com/?p=271">stance on pymel already</a> so I won&#8217;t go into it.  Honestly this exact case is probably the best reason to commit to pymel.  Those who have already made the transition to it possibly don&#8217;t even know what I&#8217;m talking about.</p>
<p>Anyway &#8211; the fix is easy of course.  And there are a bunch of ways you can go with it.  I went the route of casting the &#8220;<strong>someNode</strong>&#8221; <a title="Winning The Perf War" href="http://www.macaronikazoo.com/?p=323">object to an MObject</a> at the start of the function, but I guess you could also detect the name clash before creating the new node and modify its name accordingly &#8211; which I guess is the only option you have if you&#8217;re using mel.  But it was a frustrating half hour of my life that I&#8217;ll never get back!  ;)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.macaronikazoo.com/?feed=rss2&#038;p=630</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
