Thursday, September 22, 2011

Python Decorators

So it came to my attention yesterday that the use of decorators in python is not extremely well known. A couple years ago I had discovered them and have been using them whenever I can. Decorators are one of those things that you don’t realize you need until you know learn about it.
So what exactly is a decorator?

From the python wiki:
A decorator is the name used for a software design pattern. Decorators dynamically alter the functionality of a function, method, or class without having to directly use subclasses or change the source code of the function being decorated.

To put it simply though, a decorator allows you to execute code before and after a function is called, effectively wrapping the function around other code, dynamically.

For a more practical example, lets using a simple, common task that a python developer would use, timing a function. Without the use of decorators there are multiple ways you can do this, most involve duplicating code. If you want to time a lot of functions, you could be potentially duplicating many lines of code and altering your original source code. Adding and removing the timing functionality could be a very tedious task at the end of the day.

This is what I see timing code most commonly look like in developers who do not know about decorators:

import time
def func_a(*args):
start_time = time.time()
...
end_time = time.time()
print end_time-start_time


Now while the duplication of code isn’t too horrible, imagine if you have hundreds of functions and want to time all of them, you’d have to add that code to all functions you want to time. Quite a daunting task on a large script.
How do I use decorators?

Setup for decorators is actually really simple. Setup consists of two parts, the decorator function and adding the decorator to the function.
Decorator Function

Keeping with the timing example above, the function for a decorator is quite simple. The decorator is a function that takes a function as an argument. Inside of that function is another function that does the wrapping. Using the timing example, this is what a decorator function would look like:

def print_timing(func):
def wrapper(*arg, **kwds):
# Start the time
t1 = time.time()

# Run the function with the same arguments passed in to the original function
res = func(*arg, **kwds)

# Stop the time
t2 = time.time()

# Tell me how long it took
print '%s took %0.3f s' % (func.func_name, (t2-t1))

return res
return wrapper


Now that we have our decorator function, we simply need to decorate the functions which we want to wrap. The python phrase for this is @. In our example it would be @print_time. To decorate the function we place @print_timing on the line directly above the def of the function we want to time. For example:

@print_timing
def func_a(*args):
...

So there you have it, you can now decorate all the functions you want. No matter where the function is called from, python will dynamicaly alter the execution and run print_timing instead.

The issue of decorators was actually brought up to resolve an issue with OpenGL effecting the redraw of WX elements that were being dynamically updated. A decorator function was used to solve the issues, when I had mentioned the solution to a collegue of mine, he had never heard of decorators before, which brought up this whole post.

Enums in Python

As it stands right now, there is no built in functions for enums in Python like there are in many other programming and scripting languages. Enum’s come in handy when you need to set flags or quickly compare objects. A quick search came up with a class and a function that pretty much emulates Enums inside of python. I’ve already started using it in my TCP/IP project I’m working on and it’s working great. Wish I would have found this sooner. Pretty soon I’ll be posting a quick little walk-through of sockets and TCP/IP in python that uses the enum example below. In the past couple of weeks I’ve come to see the huge potential of using sockets in tools, but I’ll cover all that in a different post.

def M_add_class_attribs(attribs):
def foo(name, bases, dict_):
for v, k in attribs:
dict_[k] = v
return type(name, bases, dict_)
return foo

def enum(names):
class Foo(object):
__metaclass__ = M_add_class_attribs(enumerate(names))
def __setattr__(self, name, value): # this makes it read-only
raise NotImplementedError
return Foo()

# Message Type Enum
message_types = enum(("MESSAGE", "STATUS", "NONE"))

# Check the message type against a member of the enum
if (message_type == message_types.MESSAGE):
pass

Getting the angle of two edges

Today I found another issue with my cap piece creation that deals with verts that were placed on an edge and are basically isolated. Some of our game assets require this so I couldn’t just remove isolated verticies, but I needed to come up with a solution to ignore these verts when I am looking for the closest vert to a position. My solution was to determine the angles of the two edges that a vert creates and if the angle came out to be 180° then I know that that vert is a t junction vert.

I figured it be helpful to explain how to determine the angle of two edges, I’ll go over the math for it then show a code example in max.

The first step is to get the two positions of the points that make up your edge. We need this to determine the vector of the edge. The image below shows where each point is, the angle we are trying to determine and the vectors we will determine.



The first step is to determine Vector_A. To determine Vector_A you normalize the result of Position_B – Position_A. The second step is determine Vector_B, this is determined by normalizing the result of Position_C – Position_A.

Now that we have our two vectors, we need to get the dot product of Vector_A and Vector_B. Once we have that, we determine the angle by getting the arc cosign of our dot product. The result of that will be the angle that your two vectors/edges create.

Now for the maxscript:

-- Get the vert position
central_vert_pos = polyOp.getVert

-- Determine if this is a junction vert
edge_list = polyOp.getEdgesUsingVert

if (edge_list.numberSet == 2) then (
edge_verts = #{}

-- Get all the non-central edge verts
for current_edge in edge_list do (
central_vert_list = polyOp.getVertsUsingEdge current_edge
central_vert_list = central_vert_list - #{}
edge_verts += central_vert_list
)

edge_verts_array = (edge_verts as array)

-- Get the vert positions
vert_b_pos = polyOp.getVert edge_verts_array[1]
vert_c_pos = polyOp.getVert edge_verts_array[2]

-- Get the edge vectors
edge_a_vector = normalize (vert_b_pos - central_vert_pos)
edge_b_vector = normalize (vert_c_pos - central_vert_pos)

-- Get the dot product of the two edges
edge_dot = dot edge_a_vector edge_b_vector

-- Get the arc cos of the dot
edge_angle = acos edge_dot
)

MaxScript Set Face Smoothing Groups

One of the artists asked for a script to set all UV Shell’s to different smoothing groups awhile back. Today I finally got the time to take a look at it. Pulling some of the code from TexTools to get UV Shell Elements gave me what I needed to get the Poly Faces in each UV Shell. When I started working on setting the smoothing groups for the UV Shells, all appeared to work, except that I ended up with only 6 smoothing groups on each object. I tried a few different things thinking it was either modifier panel weirdness or I was just applying them wrong. After watching the MaxScript listener while manually setting smoothing groups I realized what I was doing wrong. PolyOp.SetFaceSmoothGroup doesn’t take the integer equivalent of the smoothing group that you want to set, it takes a bit flag. Looking in the MaxScript documentation confirmed this, but it is easy to miss if you aren’t just skimming through it.

So, for anyone who runs into this, the solution is simple

––Set the bit flag for the smoothing group

bit_flag = bit.set 0 true

polyOp.SetFaceSmoothGroup bit_flag