Thursday, July 30, 2009

Interesting Travel Dataset

I was looking at NavIt, an open source mapping and routing package. They use the Open Street Map for one source database. The interesting thing buried in the Open Street Map project is over a quarter of a million XML source data traces which includes location and time stamps uploaded by users.

Saturday, July 25, 2009

Interesting Dataset: Residential Energy Consumption Survey

This is an interesting dataset which illustrates the energy usage in US households annually. 2005 Residential Energy Consumption Suvey

Sunday, July 19, 2009

How to draw a curve in Blender using Python - A Quick-N-Dirty Example

In a previous example, curves were generated using cylinder and spheres. This is computationally expensive and does not fully leverage Blender's capabilities. Rather than building curves using primitive meshes, the Blender.Curve objects can be used. This example demonstrates a simple script which creates a curve in 3D using Blender curves.

Code:
Download Code
#!BPY
__doc__ = """
curveTest_xxxx.py
Rev 1.0

The purpose of this script is to demonstrate the use of blender curves.

This script is executed at the command line by:
>blender -P curveTest_xxxx.py
"""
__author__ = "edt"
__version__ = "1.0 2009/07/19"
__url__="Website, dataOrigami.blogspot.com"


##############################################################
# load the modules used in the script
from Blender import Curve, Object, Scene
from Blender.Scene import Render 
from Blender import *

##############################################################
# Get rid of the cube from the default scene
scene = Scene.GetCurrent() 
for ob in scene.objects:
   if (cmp(ob.getName(),'Cube')==0):
     scene.objects.unlink(ob)
##############################################################
# misc setup

cu = Curve.New()             # create new  curve data
scn = Scene.GetCurrent()    # get current scene
ob = scn.objects.new(cu)     # make a new curve from the curve data

##############################################################

def bezList2Curve(bezier_vecs):
    '''
    Take a list or vector triples and converts them into a bezier curve object
    '''
    
    def bezFromVecs(vecs):
      '''
      Bezier triple from 3 vecs, shortcut functon
      '''
      bt= BezTriple.New(vecs[0].x, vecs[0].y, vecs[0].z,                        
     vecs[1].x, vecs[1].y, vecs[1].z,
     vecs[2].x, vecs[2].y, vecs[2].z)
      bt.handleTypes= (BezTriple.HandleTypes.FREE, BezTriple.HandleTypes.FREE)
      return bt
    
    # Create the curve data with one point
    cu= Curve.New()
    cu.appendNurb(bezFromVecs(bezier_vecs[0])) # We must add with a point to start with
    cu_nurb= cu[0] # Get the first curve just added in the CurveData
    i= 1 # skip first vec triple because it was used to init the curve
    while i<len(bezier_vecs):
      bt_vec_triple= bezier_vecs[i]
      bt= bezFromVecs(bt_vec_triple)
      cu_nurb.append(bt)
      i+=1
    
    # Add the Curve into the scene
    cu.setFlag(cu.getFlag() | 1)  # this fixes a visibility issue with curves in z axis
    scn= Scene.GetCurrent()
    ob = scn.objects.new(cu)
    return ob

##############################################################
# class to make it easier to pass info to the curve function
    
from Blender.Mathutils import *
class Point(object):
 def __init__(self,x,y,z):
  self.x = x
  self.y = y
  self.z = z

##############################################################
# create the curve of interest from Bezier knots and handles
 
# bezier points -> handle, knot, handle 
bezier_vecs = [ [Point(1,0,0),Point(-1.1,0,0),Point(1,0,0)],
    [Point(1,1,0),Point(1.1,1.1,0),Point(1,1,0)],
    [Point(0,1,0),Point(0,1.1,0),Point(0,1,0)],
    [Point(1,0,1),Point(1.1,1.1,1.1),Point(1,0,1)]]
ob = bezList2Curve(bezier_vecs)
mat = Material.New('curveMat') # create a new Material called 'newMat' 
mat.rgbCol = [1.0, 1.0, 0.0]      # change its color 
ob.getData(False,True).materials += [mat]
ob.getData(False,True).ext1 = 0.1

##############################################################
# draw axes for reference

bezier_vecs = [ [Point(-2,0,0),Point(-2,0,0),Point(-2,0,0)],
    [Point(2,0,0),Point(2,0,0),Point(2,0,0)]]
ob = bezList2Curve(bezier_vecs)
mat = Material.New('curveMat') # create a new Material called 'newMat' 
mat.rgbCol = [1.0, 1.0, 1.0]      # change its color 
ob.getData(False,True).materials += [mat]
ob.getData(False,True).ext1 = 0.1

bezier_vecs = [ [Point(0,-2,0),Point(0,-2,0),Point(0,-2,0)],
    [Point(0,2,0),Point(0,2,0),Point(0,2,0)]]
ob = bezList2Curve(bezier_vecs)
ob.getData(False,True).materials += [mat]
ob.getData(False,True).ext1 = 0.1

bezier_vecs = [ [Point(0,0,-2),Point(0,0,-2),Point(0,0,-2)],
    [Point(0,0,2),Point(0,0,2),Point(0,0,2)]]
ob = bezList2Curve(bezier_vecs)
ob.getData(False,True).materials += [mat]
ob.getData(False,True).ext1 = 0.1

#######################################
# add spheres to points in space to make the gridding understandable
# and make it easier to see what the curve is doing.

xList = range(-2,3,2)
yList = range(-2,3,2)
zList = range(-2,3,2)

for x in xList:
 for y in yList:
  for z in zList:
   markerMe = Mesh.Primitives.UVsphere(8,8,0.3/2.0)
   A = Matrix(
    [1,0,0,0],
    [0,1,0,0],
    [0,0,1,0],
    [x,y,z,1]) 
   markerMe.transform(A,True)
   if x==0 and y==0 and z==0:
    mat2 = Material.New('curveMat') # create a new Material called 'newMat' 
    mat2.rgbCol = [0.0, 0.0, 1.0] 
    markerMe.materials += [mat2]
   else:
    markerMe.materials += [mat]
   markerOb = scene.objects.new(markerMe,'marker')

#######################################
# render the image and save it
#
context = scn.getRenderingContext() 
# enable seperate window for rendering 
Render.EnableDispWin() 
context.imageType = Render.JPEG
# draw the image 
context.render() 
# save the image to disk 
# to the location specified by RenderPath 
# by default this will be a jpg file 
context.saveRenderedImage('CurveExample.jpg') 

Window.RedrawAll() 
#
########################################

To copy the code snippets easily, see:http://dataorigami.blogspot.com/2009/06/how-to-easily-copy-code-snippets-from.html

To execute the script in Blender, see:http://dataorigami.blogspot.com/2009/06/how-to-execute-script-from-command-line.html

To work with the script in a simple IDE, see:http://dataorigami.blogspot.com/2009/04/developing-scripts-for-blender.html

Friday, July 3, 2009

Quick-N-Dirty: A rough example of an interactive data visualization in the Blender Game Engine

This example combines the Blender Game Engine example with the graphing example to allow simple navigation of the camera while looking at a graph. This example needs some fine tuning to improve the performance. The graph has too many elements to render quickly. However, it does demonstrate how to get all of the elements to work together in a data visualization. To run this example, construct the blender file described in this link. Save this file to the '\tmp' directory. Copy the code below to a python file and execute from the command line. For detailed instructions for each of these steps, see the links at the end of the posting. Once the script runs, Blender should open and draw the graph in the 3D window with the view defaulted to the camera view. Press the 'P' key to start the Blender Game Engine. Code:
#!BPY

__doc__ = """
PlotExample02.py

Example demonstrating the following techniques in Blender 2.48:
1) Use of the transform matrix
2) Use of material properties to control how objects are rendered
3) Delete (unlink) and replacement of default objects in a scene
4) Use of cross products to generate a basis for the transform matrix
5) Using python to render an image
6) Using python to save the rendered image to disk
7) Use of Blender Vector and Matrix classes
8) Defining a function in Python
9) Adding text using text3d
10) Sizing text to a box using getBoundingBox()

This script is executed at the command line by:
>blender -P plotExample02.py
"""
__author__ = "edt"
__version__ = "1.0 2009/07/01"
__url__="Website, dataOrigami.blogspot.com"

##########################################################3
import Blender
import bpy
from Blender import *
from Blender.Scene import Render
from Blender import Text

##############################################################
# load the template blender file
Blender.Load('c:\\tmp\\bgeExample004.blend')

##############################################################
# load the modules used in the script
import Blender
import bpy
from Blender import *
from Blender.Scene import Render
from Blender import Text
from Blender import Mathutils
from Blender.Mathutils import *
import math

##############################################################
# define function(s)

def frange(start, end=None, inc=None):
    "A range function, that does accept float increments..."

    if end == None:
        end = start + 0.0
        start = 0.0

    if inc == None:
        inc = 1.0

    L = []
    while 1:
        next = start + len(L) * inc
        if inc < 0 and next >= end:
            break
        elif inc < 0 and next <= end:
            break
        L.append(next)
      
    return L


def lineSegMe(p1,p2,dia=0.1,verts=16):
 """
 This function returns a mesh which forms a line from point p1 to p2.
 The points can be passes as either blender vectors or lists of [x,y,z] points.
 This line is cylinder which goes from point p1 to p2.
 Optionally the diameter and number of vertices used to describe the line are passed.
 -------------------
 The line is formed by creating a cylinder with a length equal to the distance
 point p1 and p2. The line is then oriented using the transform matrix to rotate
 and translate the cylinder.
 """
 # use the class constructors from Blender to form vectors for p1 and p2
 p1 = Vector(p1)
 p2 = Vector(p2)

 # form a vector that points in the direction from p1 to p2
 dir = p2-p1                

 # get the length of the line we want that goes from p1 to p2
 length = dir.length

 # use Mesh.Primitives.Cylinder to create a mesh for the line
 me = Mesh.Primitives.Cylinder(verts,dia,length)

 ###############
 # in the next few steps, the direction vector is used to form a basis
 #      see http://en.wikipedia.org/wiki/Basis_(linear_algebra)
 # which allows us to create a transform matrix to rotate the cylinder
 # along the direction we want. The basic idea is that the vector from
 # p1 to p2 points in the direction we want. The cylinder created by
 # Mesh.Primitives.Cylinder is oriented along the z-axis. To rotate the
 # cylinder, we  # rotate the z-axis in this direction. To completely specify
 # how to rotate, we need to provide information on how to rotate the x and y axes.
 # To define this, a matrix which is orthonormal (see http://en.wikipedia.org/wiki/Orthonormal)
 # is created from the direction vector. To create the other vectors in the
 # orthonormal basis, cross products are used to find orthogonal vectors.
 #
 # use the normalize function to set the length of the direction vector to 1
 dir.normalize()
 u = dir
 uu = Vector([0,0,1.0])
 #print AngleBetweenVecs(u,uu)
 if (abs(AngleBetweenVecs(u,uu))%180.0)>1e-3:
  # the direction of the line is different
  # from the z-axis
  # find the orthonormal basis
  v = CrossVecs(u,uu)
  w = CrossVecs(u,v)
  # form the transform matrix:
  #   > The first 3 rows and 3 columns form
  #   a rotation matrix because the any vertex transformed by this
  #   matrix will be the same distance from the origin as the original
  #   vertex. If this property is not preserved, then any shape formed
  #   will be skewed and scaled by the transform.
  #   > The first 3 columns in the last row define the translation
  #   applied to any vertex. In this function, the translation move the
  #   moves the end of the cylinder to the origin, then moves the end
  #   to p1.
  A = Matrix(
    [w[0],w[1],w[2],0],
    [v[0],v[1],v[2],0],
    [u[0],u[1],u[2],0],
    [dir[0]/2.0*length+p1[0],dir[1]/2.0*length+p1[1],dir[2]/2.0*length+p1[2],1])
 else:
  # the direction of the line is parallel to the z-axis
  # see the notes above on how the matrix is formed.
  A = Matrix(
    [1,0,0,0],
    [0,1,0,0],
    [0,0,1,0],
    [dir[0]/2.0*length+p1[0],dir[1]/2.0*length+p1[1],dir[2]/2.0*length+p1[2],1])

 # apply the transform to the cylinder 
 me.transform(A,True)
 return me

def curve(pList,color=[1.0,1.0,1.0],dia=0.1,verts=4):
 lineMeList = []
 mat = Material.New('lineMat')  # create a new Material for the line
 mat.rgbCol = color     # change the color of the line
 for i in range(0,len(pList)-1):
  p1 = pList[i]
  p2 = pList[i+1]
  lineMe = lineSegMe(p1,p2,dia,verts)
  lineMe.materials += [mat]
  lineMeList.append(lineMe)
  # check to see if another line segment will follow
  if i<len(pList)-2:
   jointMe = Mesh.Primitives.UVsphere(verts,verts,dia/2.0)
   A = Matrix(
    [1,0,0,0],
    [0,1,0,0],
    [0,0,1,0],
    [p2[0],p2[1],p2[2],1])
   jointMe.transform(A,True)
   jointMe.materials += [mat]
   lineMeList.append(jointMe)
 return lineMeList


def scrubScene(saveList=[]):
 'removes all objects in scene, except objects in save list'
 scene = Scene.GetCurrent()
 for ob in scene.objects:
  if not ob.getName() in saveList:
   scene.objects.unlink(ob)

def combineMeshesIntoOb(meList,obName):
 'adds all meshes in meList into a single object in the scene'
 # TODO: add logic to detect empty list
 # TODO: add logic to enable/disable joining
 join = False
 rooted = False
 for me in meList:
  if not rooted:
   combinedOb= scene.objects.new(me,obName)
   rooted = True
  else:
   localCombinedOb = scene.objects.new(me,'local'+obName)
   if join:
    combinedOb.join([localCombinedOb])
    scene.objects.unlink(localCombinedOb)
 return combinedOb

def textInBox(txtStr="defaultText",col=[1.0,1.0,1.0],width=1.0,height=1.0):
 txt = Text3d.New()
 txt.setText(txtStr)
 txt.setSize(0.1)
 mat=Material.New('textMat')
 mat.rgbCol = col
 ob = scene.objects.new(txt)
 me = Blender.Mesh.New('textMesh')
 me.getFromObject(ob)
 me.materials += [mat]
 scene.objects.unlink(ob)
 newOb = scene.objects.new(me)

 # force a redraw to ensure that the bounding box is updated!!
 Window.RedrawAll()
 boundBox = newOb.getBoundBox(1)
 upperBox = max(boundBox)
 lowerBox = min(boundBox)
 initialWidth = upperBox[0]-lowerBox[0]
 initialHeight = upperBox[1]-lowerBox[1]
 widthRatio = width/initialWidth
 heightRatio = height/initialHeight
 ratio = min(widthRatio,heightRatio)

 newOb.setSize(ratio,ratio,ratio)
 newOb.setMaterials([mat])
 return newOb


##############################################################
##############################################################
##############################################################
# name for the embedded script in the loaded file
gameScriptName = 'MainScript.py'

##############################################################
# Get rid of the lamp and cube from the default scene
#     - leave the camera, it has the linkages for the scripting
#       for the BGE.
#     - Note: Do NOT make any object the BGE scripts invisible
#       via ob.layers=[], this will disable the scripts
#       attached to the object.
scene = Scene.GetCurrent()
for ob in scene.objects:
   print ob.getName()
   if ((cmp(ob.getName(),'Lamp')==0)
    (cmp(ob.getName(),'Cube')==0)):
  scene.objects.unlink(ob)


##############################################################
# find camera in file and set it up
#   
#camdata = Camera.New()
#cam = scene.objects.new(camdata)
cam = Blender.Object.Get('Camera')
# use setLocation to control the position of the camera
cam.setLocation(0.9,0.5,2.1)
# use set Euler to control the angle of the camera
cam.setEuler(0*(3.1415/180),10*(3.1415/180),0*(3.1415/180))
scene.objects.camera = cam
##############################################################
# add a lamp and set it up
#
lampData = Lamp.New()
lampData.setEnergy(1.0)
lampData.setType('Lamp')
lampData.mode = Lamp.Modes["RayShadow"]  # make shadows appear
lamp = scene.objects.new(lampData)
lamp.setLocation(2.0,2.0,5.0)
lamp.setEuler(120*(3.1415/180),30*(3.1415/180),-30*(3.1415/180))
##############################################################
# load the data
#
f = open('c:\\tmp\\EmploymentPopRatio.txt', 'r')
foundData = False
data = {'x':[],'y':[],'z':[]}
labels = {'x':[],'y':[],'z':[]}
for line in f:
 fields = line.split(',')
 if not foundData:
  for entry in fields:
   if entry == 'Year':
    # this is the header row of data
    foundData = True
 else:
  print len(line)
  if len(line)>1:
   for i in range(0,13):
    if i==0:
     print line
     print fields[0]
     year =  float(fields[0])
    else:
     month = i
     if not fields[i]==' ':
      print '::'+fields[i]+''
      data['x'].append(float(year+(month-1)/12.0))
      labels['x'].append(str(year)+','+str(month))
      data['y'].append(float(fields[i]))
      data['z'].append(0.05)
 

#############################
#############################

axisList = ['x','y','z']


# setup the data ranges

graphSetup = {}
graphSetup["x"]={'max':2010,'min':1940,'inc':10}
graphSetup["y"]={'max':70,'min':50,'inc':5}
graphSetup["z"]={'max':1,'min':0,'inc':1}
graphSetup["title"]='Employment to Population Ratio'
graphSetup["xLabels"]=[]
for x in frange(graphSetup['x']['min'],graphSetup['x']['max']+graphSetup['x']['inc'],graphSetup['x']['inc']):
 graphSetup['xLabels'].append(str(x))

print graphSetup['xLabels']

# scale the data for presentation
scale={}
offset={}
for axis in axisList:
 scale[axis]=1.0/float(graphSetup[axis]['max']-graphSetup[axis]['min'])
 offset[axis] = float(graphSetup[axis]['min'])

# build point list
pointList = []
for i in range(0,len(data['x'])):
 p = {}
 for axis in scale.keys():
  p[axis]=(data[axis][i]-offset[axis])*scale[axis]
 point = Vector([p['x'],p['y'],p['z']])
 pointList.append(point)

##############################################################
# create the objects in the scene and bind materials to them
# draw the data from the graph
meList = curve(pointList,[1.0,0.1,0.4],0.01,8)
combineMeshesIntoOb(meList,'curveOb')
#
##############################
# create a grid for the different axes
#
tics = {}
scaledTics = {'x':[],'y':[],'z':[]}
for axis in scaledTics.keys():
 tics[axis]=frange(graphSetup[axis]['min'],
      graphSetup[axis]['max']+graphSetup[axis]['inc'],
      graphSetup[axis]['inc'])
 for tic in tics[axis]:
  scaledTics[axis].append((tic-offset[axis])*scale[axis])

# draw x-y axes

def plotGrid(tics={'x':[],'y':[],'z':[]},axes=['x','y'],col=[1.0,1.0,0.0],isTics=False):
 ''
 def plotGridLines(tics,axis,P1,P2,col):
  'plot the grid elements'
  for tic in tics[axis]:
   R = Vector([tic,1.0,0.0])
   point1 = P1*R
   point2 = P2*R
   pointList = [point1,point2]
   meList = curve(pointList,col,0.01,8)
   combineMeshesIntoOb(meList,'GridElement')
 #
 def plotGridCorners(cornerLocs,col):
  mat = Material.New('axisMat') # create a new Material called 'newMat'
  mat.rgbCol = col
  for i in range(0,len(cornerLocs)):
   cornerMe = Mesh.Primitives.UVsphere(32,32,0.01)
   cornerOb = scene.objects.new(cornerMe,'originOb')
   cornerOb.getData(False,True).materials+=[mat]
   cornerOb.setLocation(cornerLocs[i][0],cornerLocs[i][1],cornerLocs[i][2])
 #
 if isTics:
  gridLen = 0.1
 else:
  gridLen = 1.0
 if 'x' in axes and 'y' in axes:
  P1 = Matrix([1.0,0.0,0.0],[0.0,0.0,0.0],[0.0,0.0,0.0])
  P2 = Matrix([1.0,0.0,0.0],[0.0,gridLen,0.0],[0.0,0.0,0.0])
  plotGridLines(tics,'x',P1,P2,col)
  P1 = Matrix([0.0,gridLen,0.0],[1.0,0.0,0.0],[0.0,0.0,0.0])
  P2 = Matrix([0.0,0.0,0.0],[1.0,0.0,0.0],[0.0,0.0,0.0])
  plotGridLines(tics,'y',P1,P2,col)
  if isTics:
   cornerLocs =[[0,0,0]]
  else:
   cornerLocs =[[0,0,0],[0,1,0],[1,1,0],[1,0,0]]
  plotGridCorners(cornerLocs,col)
 elif 'x' in axes and 'z' in axes:
  P1 = Matrix([1.0,0.0,0.0],[0.0,0.0,0.0],[0.0,0.0,0.0])
  P2 = Matrix([1.0,0.0,0.0],[0.0,0.0,0.0],[0.0,gridLen,0.0])
  plotGridLines(tics,'x',P1,P2,col)
  P1 = Matrix([0.0,0.0,0.0],[0.0,0.0,0.0],[1.0,0.0,0.0])
  P2 = Matrix([0.0,gridLen,0.0],[0.0,0.0,0.0],[1.0,0.0,0.0])
  plotGridLines(tics,'z',P1,P2,col)
  if isTics:
   cornerLocs =[[0,0,0]]
  else:
   cornerLocs =[[0,0,0],[0,1,0],[0,1,1],[0,0,1]]
  plotGridCorners(cornerLocs,col)
 elif 'y' in axes and 'z' in axes:
  P1 = Matrix([0.0,0.0,0.0],[1.0,0.0,0.0],[0.0,0.0,0.0])
  P2 = Matrix([0.0,0.0,0.0],[1.0,0.0,0.0],[0.0,gridLen,0.0])
  plotGridLines(tics,'y',P1,P2,col)
  P1 = Matrix([0.0,0.0,0.0],[0.0,gridLen,0.0],[1.0,0.0,0.0])
  P2 = Matrix([0.0,0.0,0.0],[0.0,0.0,0.0],[1.0,0.0,0.0])
  plotGridLines(tics,'z',P1,P2,col)
  if isTics:
   cornerLocs =[[0,0,0]]
  else:
   cornerLocs =[[0,0,0],[0,1,0],[1,1,0],[1,0,0]]
  plotGridCorners(cornerLocs,col)

plotGrid(scaledTics,axes=['x','y'],col=[1.0,1.0,0.0],isTics=False)
#plotGrid(scaledTics,axes=['x','z'],col=[1.0,1.0,0.0],isTics=False)
#plotGrid(scaledTics,axes=['y','z'],col=[1.0,1.0,0.0],isTics=False)

##############################
# create x-axis labels
#
for i in range(0,len(graphSetup['xLabels'])):
 label = graphSetup['xLabels'][i]
 xLoc = scaledTics['x'][i]
 xTicOb=textInBox(label,[1.0,0.0,0.0],0.1,0.1)
 xTicOb.setLocation(xLoc,-0.01,0.05)
 xTicOb.setEuler(0.0,0.0,-90.0*(3.1415/180.0))

##############################
# create y-axis labels
#
for i in range(0,len(tics['y'])):
 yLabel = str(tics['y'][i])+'%'
 yLoc = scaledTics['y'][i]
 yTicOb=textInBox(yLabel,[1.0,0.0,0.0],0.1,0.1)
 yTicOb.setLocation(-0.11,yLoc-0.025,0.05)

##############################
# create a title
#
titleOb=textInBox(graphSetup['title'],[1.0,0.0,0.0],1.0,1.0)
titleOb.setLocation(0.0,1.05,0.05)

##############################
#
mat = Material.New('xyBackMat') # create a new Material called 'newMat'
mat.rgbCol = [1.0, 1.0, 1.0]      # change its color
xyBackMe = Mesh.Primitives.Grid(2,2)
A = Matrix(
 [0.5,0,0,0],
 [0,0.5,0,0],
 [0,0,0.5,0],
 [0.5,0.5,-0.05,1])
xyBackMe.transform(A,True)
xyBackOb = scene.objects.new(xyBackMe,'xyBackMe')
xyBackOb.getData(False,True).materials += [mat]
########################################
########################################
#######################################
# setup the event handler to animate the polygon
#
# create the text that handles the event
gameScript = """
#######################################
# tasks for each iteration thru this script
# 1) Output diagnostic information to console
# 2) pan the camera in response to the mouse and keyboard
#    a) x,y controlled by arrow keys
#    b) rotation controlled by shift arrow keys
#    c) mouse moves in x,y
#    d) shift mouse cause rotation
# 3) rotate the dislayed objects at a fixed rate using system time

print 'BGE script executing...'

import Blender
print Blender.sys.time()

# GameLogic has been added to the global namespace no need to import

# for keyboard event comparison
import GameKeys

# support for Vector(), Matrix() types and advanced functions like AngleBetweenVecs(v1,v2) and RotationMatrix(...)
import Mathutils

# for functions like getWindowWidth(), getWindowHeight()
import Rasterizer

# for matrix operations
import Mathutils

def main():
 print 'linked script started'
 frame = Blender.Get('curframe')
 sc    = Blender.Scene.GetCurrent()
 print 'linked script completed - Frame %d' % frame
 frame = frame+1
 Blender.Set('curframe',frame)

 cont = GameLogic.getCurrentController()

 # The KX_GameObject that owns this controller.
 own = cont.getOwner()

 # for scripts that deal with spacial logic
 own_pos = own.getPosition()


 # Some example functions, remove to write your own script.
 # check for a positive sensor, will run on any object without errors.
 print 'Logic info for KX_GameObject', own.getName()
 input = False

 for sens in cont.getSensors():
  # The sensor can be on another object, we may want to use it
  own_sens = sens.getOwner()
  print '    sensor:', sens.getName(),
  if sens.isPositive():
   print '(true)'
   input = True
  else:
   print '(false)'
  sensName = sens.getName()
  if sensName=='sMouse':
   print '..mouse x = '+ str(sens.getXPosition())
   print '..mouse y = '+ str(sens.getYPosition())
  if (sensName=='sKeyboard') and (sens.isPositive()):
   keyList = sens.getCurrentlyPressedKeys()
   keyStr = ''
   for key in keyList:
    keyStr = keyStr + str(key[0]) + ','
   print '..key     = '+keyStr

 for actu in cont.getActuators():
  # The actuator can be on another object, we may want to use it
  own_actu = actu.getOwner()
  print '    actuator:', sens.getName()

  # This runs the actuator or turns it off
  # note that actuators will continue to run unless explicitly turned off.
  if input:
   GameLogic.addActiveActuator(actu, True)
  else:
   GameLogic.addActiveActuator(actu, False)

 # Its also good practice to get sensors and actuators by names
 # so any changes to their order wont break the script.

 sce = GameLogic.getCurrentScene()
 ob   = sce.getObjectList()['OBCamera']
 sens = cont.getSensor('sKeyboard')
 if sens.isPositive():
  print '--key pressed--'
  fullKeyList = sens.getCurrentlyPressedKeys()
  keyList=[]
  for key in fullKeyList:
   keyList.append(key[0])
  pos = ob.getPosition()
  orientL = ob.getOrientation()
  orientM = Mathutils.Matrix(orientL[0],orientL[1],orientL[2])
  orientM.transpose()
  orient  = orientM.toEuler()
  if (GameKeys.RIGHTSHIFTKEY in keyList) or (GameKeys.LEFTSHIFTKEY in keyList):
   print '--Shift Pressed--'
   if GameKeys.RIGHTARROWKEY in keyList:
    orient[1]=orient[1]+0.5
    print '--Right Arrow Key--'
   elif GameKeys.LEFTARROWKEY in keyList:
    orient[1]=orient[1]-0.5
    print '--Left Arrow Key--'
   if GameKeys.UPARROWKEY in keyList:
    orient[0]=orient[0]+0.5
    print '--Up Arrow Key--'
   elif GameKeys.DOWNARROWKEY in keyList:
    orient[0]=orient[0]-0.5
    print '--Down Arrow Key--'
   else:
    print '--other key--'
  else:
   if GameKeys.RIGHTARROWKEY in keyList:
    pos[0]=pos[0]-0.02
    print '--Right Arrow Key--'
   elif GameKeys.LEFTARROWKEY in keyList:
    pos[0]=pos[0]+0.02
    print '--Left Arrow Key--'
   if GameKeys.UPARROWKEY in keyList:
    pos[1]=pos[1]-0.02
    print '--Up Arrow Key--'
   elif GameKeys.DOWNARROWKEY in keyList:
    pos[1]=pos[1]+0.02
    print '--Down Arrow Key--'
   else:
    print '--other key--'
  orientM = orient.toMatrix()
  orientM.transpose()
  ob.setOrientation(orientM)
  ob.setPosition(pos)
 #ob.setPosition([frame/20.0,0.0,0.0])

main()
"""
#EVENT = "FrameChanged"
txt = Text.Get(gameScriptName) #get the gamescript
txt.clear()                    # clear the existing script
txt.write(gameScript)          # appending text

#######################################
# set 3-d view to use the camera
Blender.Window.CameraView()
########################################

To copy the code snippets easily, see:http://dataorigami.blogspot.com/2009/06/how-to-easily-copy-code-snippets-from.html

To execute the script in Blender, see:http://dataorigami.blogspot.com/2009/06/how-to-execute-script-from-command-line.html

To work with the script in a simple IDE, see:http://dataorigami.blogspot.com/2009/04/developing-scripts-for-blender.html

Wednesday, July 1, 2009

How to size and place text in Blender using Python

This example shows how to size and place text arbitrarily in the Blender environment. The Blender API offers a function to size text, however, it is limited to a minimum range of '0.1'. For some applications this limitation is undesirable. This example includes a function, 'textInBox', which generates the text so that it fits in a box of given height and width. So the box can be arbitrarily sized, the function converts the text into a mesh, find the size of the mesh, then scales the text to fix in the box. One key aspect of this example which is not obvious, is that in order to correctly use the 'getBoundBox' function to determine the size of a mesh, the screen needs to be redrawn using 'Window.RedrawAll()'. If this is not done prior to using 'getBoundBox', the bounding box returned will be incorrect.

Code:
#!BPY

__doc__ = """
SizeAndPlaceText01.py

Example demonstrating the following techniques in Blender 2.48:
1) Sizing and placing 3-D Text
2) Using the setLocation functions

This script is executed at the command line by:
>blender -P sizeAndPlaceText01.py
"""
__author__ = "edt"
__version__ = "1.0 2009/07/01"
__url__="Website, dataOrigami.blogspot.com"


##############################################################
# load the modules used in the script
import Blender
import bpy
from Blender import * 
from Blender.Scene import Render 
from Blender import Text
from Blender import Mathutils
from Blender.Mathutils import *
import math

##############################################################
# define function(s) 

def scrubScene(saveList=[]):
 'removes all objects in scene, except objects in save list'
 scene = Scene.GetCurrent()
 for ob in scene.objects:
  if not ob.getName() in saveList:
   scene.objects.unlink(ob)

def textInBox(txtStr="defaultText",col=[1.0,1.0,1.0],width=1.0,height=1.0):
 txt = Text3d.New()
 txt.setText(txtStr)
 txt.setSize(0.1)
 mat=Material.New('textMat')
 mat.rgbCol = col
 ob = scene.objects.new(txt)
 me = Blender.Mesh.New('textMesh')
 me.getFromObject(ob)
 me.materials += [mat]
 scene.objects.unlink(ob)
 newOb = scene.objects.new(me)

 # force a redraw to ensure that the bounding box is updated!!
 Window.RedrawAll() 
 boundBox = newOb.getBoundBox(1)
 upperBox = max(boundBox)
 lowerBox = min(boundBox)
 initialWidth = upperBox[0]-lowerBox[0]
 initialHeight = upperBox[1]-lowerBox[1]
 widthRatio = width/initialWidth
 heightRatio = height/initialHeight
 ratio = min(widthRatio,heightRatio)
 
 newOb.setSize(ratio,ratio,ratio)
 return newOb

 
##############################################################
##############################################################
# clean out any objects from the scene
scrubScene()
scene = Scene.GetCurrent()

##############################################################
# add a camera and set it up
#     
camdata = Camera.New() 
cam = scene.objects.new(camdata) 
# use setLocation to control the position of the camera
cam.setLocation(10.0,8.1,20.0) 
# use set Euler to control the angle of the camera
cam.setEuler(0*(3.1415/180),0*(3.1415/180),0*(3.1415/180)) 
scene.objects.camera = cam
##############################################################
# add a lamp and set it up
#
lampData = Lamp.New() 
lampData.setEnergy(1.0) 
lampData.setType('Lamp')
lampData.mode |= Lamp.Modes["RayShadow"]  # make shadows appear
lamp = scene.objects.new(lampData) 
lamp.setLocation(2.0,2.0,9.0) 
lamp.setEuler(120*(3.1415/180),30*(3.1415/180),-30*(3.1415/180))
  
##############################
# create different size text at different locations
# 
# start at origin and move upward
yLoc = 0.0
size = 0.1
for i in range(0,20):
   ob = textInBox('Test Text',[0.0,1.0,1.0],1e9,size)
   ob.setLocation(1.0,yLoc,0.0)
   yLoc = yLoc + size + 0.005
   size = size * 1.25
##############################
# create a background so you can see that everything is at same z level
mat = Material.New('xyBackMat') # create a new Material called 'newMat' 
mat.rgbCol = [1.0, 1.0, 1.0]      # change its color 
xyBackMe = Mesh.Primitives.Grid(2,2)
A = Matrix(
 [10.0,0.0, 0.0 ,0.0],
 [0.0,10.0, 0.0 ,0.0],
 [0.0,0.0, 10.0 ,0.0],
 [10.0,10.0,-0.05,1.0])
xyBackMe.transform(A,True)
xyBackOb = scene.objects.new(xyBackMe,'xyBackMe')
xyBackOb.getData(False,True).materials += [mat]
# this makes the grid appear as an outline in the Blender editor
#xyBackOb.setDrawType(Object.DrawTypes["WIRE"])

#######################################
# render the image and save the image
context = scene.getRenderingContext() 
# enable seperate window for rendering 
Render.EnableDispWin() 
context.imageType = Render.JPEG
# draw the image 
context.render() 
# save the image to disk 
# to the location specified by RenderPath 
# by default this will be a jpg file 
context.saveRenderedImage('SizeAndPlaceTextExample001.jpg') 

Window.RedrawAll() 
#
########################################

To copy the code snippets easily, see:http://dataorigami.blogspot.com/2009/06/how-to-easily-copy-code-snippets-from.html

To execute the script in Blender, see:http://dataorigami.blogspot.com/2009/06/how-to-execute-script-from-command-line.html

To work with the script in a simple IDE, see:http://dataorigami.blogspot.com/2009/04/developing-scripts-for-blender.html

Quick-N-Dirty: A graph with labels in Blender

This example shows how to generate a 2-D graph in Blender with a 3-D appearance. This example illustrates how to
  • generate a graph from data,
  • size and position text anywhere and of any size,
  • delete (unlink) all elements from a scene including the default cube, lamp, and camera, and
  • combine multiple meshes into a single mesh.

The repetitive and complex tasks in the example are implemented using functions. To build a line segment in the graph curve in the graph, the function 'lineSegMe' generate a mesh tube that runs from one point to another. To generate a complex curve, these tubes are strung together using the 'curve' function. Between each tube, a sphere is inserted to ensure that the graph looks smooth. To remove all elements from the default scene, the 'scrubScene' function is defined. Finally, the function 'textInABox' is defined to scale a string so it fits into a box witha given height and width.

How to run the example:

  1. Copy the data file from this example to the '\tmp' directory on the machine. If the '\tmp' directory does not exist, create it.
  2. Copy the code example into 'plotExample02.py' and save the file. See the notes on working with notepad to see how to setup Notepad to automate testing and execution of Blender scripts.
  3. To execute the script, from the command line execute the following: "blender -P plotExample02.py"
Code:
#!BPY

__doc__ = """
PlotExample02.py

Example demonstrating the following techniques in Blender 2.48:
1) Use of the transform matrix
2) Use of material properties to control how objects are rendered
3) Delete (unlink) and replacement of default objects in a scene
4) Use of cross products to generate a basis for the transform matrix
5) Using python to render an image
6) Using python to save the rendered image to disk
7) Use of Blender Vector and Matrix classes
8) Defining a function in Python
9) Adding text using text3d
10) Sizing text to a box using getBoundingBox()

This script is executed at the command line by:
>blender -P plotExample02.py
"""
__author__ = "edt"
__version__ = "1.0 2009/07/01"
__url__="Website, dataOrigami.blogspot.com"


##############################################################
# load the modules used in the script
import Blender
import bpy
from Blender import * 
from Blender.Scene import Render 
from Blender import Text
from Blender import Mathutils
from Blender.Mathutils import *
import math

##############################################################
# define function(s) 

def frange(start, end=None, inc=None):
    "A range function, that does accept float increments..."

    if end == None:
        end = start + 0.0
        start = 0.0

    if inc == None:
        inc = 1.0

    L = []
    while 1:
        next = start + len(L) * inc
        if inc > 0 and next >= end:
            break
        elif inc < 0 and next <= end:
            break
        L.append(next)
        
    return L


def lineSegMe(p1,p2,dia=0.1,verts=16):
 """
 This function returns a mesh which forms a line from point p1 to p2. 
 The points can be passes as either blender vectors or lists of [x,y,z] points.
 This line is cylinder which goes from point p1 to p2.
 Optionally the diameter and number of vertices used to describe the line are passed.
 -------------------
 The line is formed by creating a cylinder with a length equal to the distance
 point p1 and p2. The line is then oriented using the transform matrix to rotate
 and translate the cylinder. 
 """
 # use the class constructors from Blender to form vectors for p1 and p2
 p1 = Vector(p1)
 p2 = Vector(p2)
 
 # form a vector that points in the direction from p1 to p2
 dir = p2-p1                  
 
 # get the length of the line we want that goes from p1 to p2
 length = dir.length

 # use Mesh.Primitives.Cylinder to create a mesh for the line
 me = Mesh.Primitives.Cylinder(verts,dia,length)
 
 ###############
 # in the next few steps, the direction vector is used to form a basis
 #      see http://en.wikipedia.org/wiki/Basis_(linear_algebra)
 # which allows us to create a transform matrix to rotate the cylinder 
 # along the direction we want. The basic idea is that the vector from
 # p1 to p2 points in the direction we want. The cylinder created by 
 # Mesh.Primitives.Cylinder is oriented along the z-axis. To rotate the 
 # cylinder, we  # rotate the z-axis in this direction. To completely specify
 # how to rotate, we need to provide information on how to rotate the x and y axes. 
 # To define this, a matrix which is orthonormal (see http://en.wikipedia.org/wiki/Orthonormal)
 # is created from the direction vector. To create the other vectors in the 
 # orthonormal basis, cross products are used to find orthogonal vectors.
 # 
 # use the normalize function to set the length of the direction vector to 1
 dir.normalize()
 u = dir
 uu = Vector([0,0,1.0])
 #print AngleBetweenVecs(u,uu)
 if (abs(AngleBetweenVecs(u,uu))%180.0)>1e-3:
  # the direction of the line is different
  # from the z-axis
  # find the orthonormal basis
  v = CrossVecs(u,uu) 
  w = CrossVecs(u,v)
  # form the transform matrix: 
  #   > The first 3 rows and 3 columns form
  #   a rotation matrix because the any vertex transformed by this 
  #   matrix will be the same distance from the origin as the original
  #   vertex. If this property is not preserved, then any shape formed 
  #   will be skewed and scaled by the transform.
  #   > The first 3 columns in the last row define the translation
  #   applied to any vertex. In this function, the translation move the 
  #   moves the end of the cylinder to the origin, then moves the end
  #   to p1.
  A = Matrix(
    [w[0],w[1],w[2],0],
    [v[0],v[1],v[2],0],
    [u[0],u[1],u[2],0],
    [dir[0]/2.0*length+p1[0],dir[1]/2.0*length+p1[1],dir[2]/2.0*length+p1[2],1])
 else:
  # the direction of the line is parallel to the z-axis
  # see the notes above on how the matrix is formed.
  A = Matrix(
    [1,0,0,0],
    [0,1,0,0],
    [0,0,1,0],
    [dir[0]/2.0*length+p1[0],dir[1]/2.0*length+p1[1],dir[2]/2.0*length+p1[2],1]) 

 # apply the transform to the cylinder   
 me.transform(A,True)
 return me

def curve(pList,color=[1.0,1.0,1.0],dia=0.1,verts=4):
 lineMeList = []
 mat = Material.New('lineMat')  # create a new Material for the line 
 mat.rgbCol = color     # change the color of the line
 for i in range(0,len(pList)-1):
  p1 = pList[i]
  p2 = pList[i+1]
  lineMe = lineSegMe(p1,p2,dia,verts)
  lineMe.materials += [mat]
  lineMeList.append(lineMe)
  # check to see if another line segment will follow
  if i<len(pList)-2:
   jointMe = Mesh.Primitives.UVsphere(verts,verts,dia/2.0)
   A = Matrix(
    [1,0,0,0],
    [0,1,0,0],
    [0,0,1,0],
    [p2[0],p2[1],p2[2],1]) 
   jointMe.transform(A,True)
   jointMe.materials += [mat]
   lineMeList.append(jointMe)
 return lineMeList

 
def scrubScene(saveList=[]):
 'removes all objects in scene, except objects in save list'
 scene = Scene.GetCurrent()
 for ob in scene.objects:
  if not ob.getName() in saveList:
   scene.objects.unlink(ob)
  
def combineMeshesIntoOb(meList,obName):
 'adds all meshes in meList into a single object in the scene'
 # TODO: add logic to detect empty list
 # TODO: add logic to enable/disable joining
 join = False
 rooted = False
 for me in meList:
  if not rooted:
   combinedOb= scene.objects.new(me,obName)
   rooted = True
  else:
   localCombinedOb = scene.objects.new(me,'local'+obName)
   if join:
    combinedOb.join([localCombinedOb])
    scene.objects.unlink(localCombinedOb)
 return combinedOb

def textInBox(txtStr="defaultText",col=[1.0,1.0,1.0],width=1.0,height=1.0):
 txt = Text3d.New()
 txt.setText(txtStr)
 txt.setSize(0.1)
 mat=Material.New('textMat')
 mat.rgbCol = col
 ob = scene.objects.new(txt)
 me = Blender.Mesh.New('textMesh')
 me.getFromObject(ob)
 me.materials += [mat]
 scene.objects.unlink(ob)
 newOb = scene.objects.new(me)

 # force a redraw to ensure that the bounding box is updated!!
 Window.RedrawAll() 
 boundBox = newOb.getBoundBox(1)
 upperBox = max(boundBox)
 lowerBox = min(boundBox)
 initialWidth = upperBox[0]-lowerBox[0]
 initialHeight = upperBox[1]-lowerBox[1]
 widthRatio = width/initialWidth
 heightRatio = height/initialHeight
 ratio = min(widthRatio,heightRatio)
 
 newOb.setSize(ratio,ratio,ratio)
 return newOb

 
##############################################################
##############################################################
# clean out any objects from the scene
scrubScene()
scene = Scene.GetCurrent()

##############################################################
# add a camera and set it up
#     
camdata = Camera.New() 
cam = scene.objects.new(camdata) 
# use setLocation to control the position of the camera
cam.setLocation(0.9,0.5,2.1) 
# use set Euler to control the angle of the camera
cam.setEuler(0*(3.1415/180),10*(3.1415/180),0*(3.1415/180)) 
scene.objects.camera = cam
##############################################################
# add a lamp and set it up
#
lampData = Lamp.New() 
lampData.setEnergy(1.0) 
lampData.setType('Lamp')
lampData.mode |= Lamp.Modes["RayShadow"]  # make shadows appear
lamp = scene.objects.new(lampData) 
lamp.setLocation(2.0,2.0,5.0) 
lamp.setEuler(120*(3.1415/180),30*(3.1415/180),-30*(3.1415/180))
##############################################################
# load the data
#
f = open('c:\\tmp\\EmploymentPopRatio.txt', 'r')
foundData = False
data = {'x':[],'y':[],'z':[]}
labels = {'x':[],'y':[],'z':[]}
for line in f:
 fields = line.split(',')
 if not foundData:
  for entry in fields:
   if entry == 'Year':
    # this is the header row of data
    foundData = True
 else:
  print len(line)
  if len(line)>1:
   for i in range(0,13):
    if i==0:
     print line
     print fields[0]
     year =  float(fields[0])
    else:
     month = i
     if not fields[i]==' ':
      print '::'+fields[i]+'||'
      data['x'].append(float(year+(month-1)/12.0))
      labels['x'].append(str(year)+','+str(month))
      data['y'].append(float(fields[i]))
      data['z'].append(0.05)
   

#############################
#############################

axisList = ['x','y','z']


# setup the data ranges

graphSetup = {}
graphSetup["x"]={'max':2010,'min':1940,'inc':10}
graphSetup["y"]={'max':70,'min':50,'inc':5}
graphSetup["z"]={'max':1,'min':0,'inc':1}
graphSetup["title"]='Employment to Population Ratio'
graphSetup["xLabels"]=[]
for x in frange(graphSetup['x']['min'],graphSetup['x']['max']+graphSetup['x']['inc'],graphSetup['x']['inc']):
 graphSetup['xLabels'].append(str(x))

print graphSetup['xLabels']
 
# scale the data for presentation
scale={}
offset={}
for axis in axisList:
 scale[axis]=1.0/float(graphSetup[axis]['max']-graphSetup[axis]['min'])
 offset[axis] = float(graphSetup[axis]['min'])

# build point list
pointList = []
for i in range(0,len(data['x'])):
 p = {}
 for axis in scale.keys():
  p[axis]=(data[axis][i]-offset[axis])*scale[axis]
 point = Vector([p['x'],p['y'],p['z']])
 pointList.append(point)

##############################################################
# create the objects in the scene and bind materials to them
# draw the data from the graph
meList = curve(pointList,[1.0,0.1,0.4],0.01,8)
combineMeshesIntoOb(meList,'curveOb')
#
##############################
# create a grid for the different axes
#
tics = {}
scaledTics = {'x':[],'y':[],'z':[]}
for axis in scaledTics.keys():
 tics[axis]=frange(graphSetup[axis]['min'],
      graphSetup[axis]['max']+graphSetup[axis]['inc'],
      graphSetup[axis]['inc'])
 for tic in tics[axis]:
  scaledTics[axis].append((tic-offset[axis])*scale[axis])

# draw x-y axes  

def plotGrid(tics={'x':[],'y':[],'z':[]},axes=['x','y'],col=[1.0,1.0,0.0],isTics=False):
 ''
 def plotGridLines(tics,axis,P1,P2,col):
  'plot the grid elements' 
  for tic in tics[axis]:
   R = Vector([tic,1.0,0.0])
   point1 = P1*R
   point2 = P2*R
   pointList = [point1,point2]
   meList = curve(pointList,col,0.01,8)
   combineMeshesIntoOb(meList,'GridElement')
 #
 def plotGridCorners(cornerLocs,col):
  mat = Material.New('axisMat') # create a new Material called 'newMat' 
  mat.rgbCol = col 
  for i in range(0,len(cornerLocs)):
   cornerMe = Mesh.Primitives.UVsphere(32,32,0.01)
   cornerOb = scene.objects.new(cornerMe,'originOb')
   cornerOb.getData(False,True).materials+=[mat]
   cornerOb.setLocation(cornerLocs[i][0],cornerLocs[i][1],cornerLocs[i][2])
 #
 if isTics:
  gridLen = 0.1
 else:
  gridLen = 1.0
 if 'x' in axes and 'y' in axes:
  P1 = Matrix([1.0,0.0,0.0],[0.0,0.0,0.0],[0.0,0.0,0.0])
  P2 = Matrix([1.0,0.0,0.0],[0.0,gridLen,0.0],[0.0,0.0,0.0])
  plotGridLines(tics,'x',P1,P2,col)
  P1 = Matrix([0.0,gridLen,0.0],[1.0,0.0,0.0],[0.0,0.0,0.0])
  P2 = Matrix([0.0,0.0,0.0],[1.0,0.0,0.0],[0.0,0.0,0.0])
  plotGridLines(tics,'y',P1,P2,col)
  if isTics:
   cornerLocs =[[0,0,0]]  
  else:
   cornerLocs =[[0,0,0],[0,1,0],[1,1,0],[1,0,0]]
  plotGridCorners(cornerLocs,col)
 elif 'x' in axes and 'z' in axes:
  P1 = Matrix([1.0,0.0,0.0],[0.0,0.0,0.0],[0.0,0.0,0.0])
  P2 = Matrix([1.0,0.0,0.0],[0.0,0.0,0.0],[0.0,gridLen,0.0])
  plotGridLines(tics,'x',P1,P2,col)
  P1 = Matrix([0.0,0.0,0.0],[0.0,0.0,0.0],[1.0,0.0,0.0])
  P2 = Matrix([0.0,gridLen,0.0],[0.0,0.0,0.0],[1.0,0.0,0.0])
  plotGridLines(tics,'z',P1,P2,col)
  if isTics:
   cornerLocs =[[0,0,0]]  
  else:
   cornerLocs =[[0,0,0],[0,1,0],[0,1,1],[0,0,1]]
  plotGridCorners(cornerLocs,col)
 elif 'y' in axes and 'z' in axes:
  P1 = Matrix([0.0,0.0,0.0],[1.0,0.0,0.0],[0.0,0.0,0.0])
  P2 = Matrix([0.0,0.0,0.0],[1.0,0.0,0.0],[0.0,gridLen,0.0])
  plotGridLines(tics,'y',P1,P2,col)
  P1 = Matrix([0.0,0.0,0.0],[0.0,gridLen,0.0],[1.0,0.0,0.0])
  P2 = Matrix([0.0,0.0,0.0],[0.0,0.0,0.0],[1.0,0.0,0.0])
  plotGridLines(tics,'z',P1,P2,col)
  if isTics:
   cornerLocs =[[0,0,0]]  
  else:
   cornerLocs =[[0,0,0],[0,1,0],[1,1,0],[1,0,0]]
  plotGridCorners(cornerLocs,col)
 
plotGrid(scaledTics,axes=['x','y'],col=[1.0,1.0,0.0],isTics=False) 
#plotGrid(scaledTics,axes=['x','z'],col=[1.0,1.0,0.0],isTics=False) 
#plotGrid(scaledTics,axes=['y','z'],col=[1.0,1.0,0.0],isTics=False) 
  
##############################
# create x-axis labels
# 



for i in range(0,len(graphSetup['xLabels'])):
 label = graphSetup['xLabels'][i]
 xLoc = scaledTics['x'][i]
 xTicOb=textInBox(label,[1.0,0.0,0.0],0.1,0.1) 
 xTicOb.setLocation(xLoc,-0.01,0.05)
 xTicOb.setEuler(0.0,0.0,-90.0*(3.1415/180.0))
  
##############################
# create y-axis labels
# 
for i in range(0,len(tics['y'])):  
 yLabel = str(tics['y'][i])+'%'
 yLoc = scaledTics['y'][i]
 yTicOb=textInBox(yLabel,[1.0,0.0,0.0],0.1,0.1)
 yTicOb.setLocation(-0.11,yLoc-0.025,0.05)
 

##############################
# create a title
# 

titleOb=textInBox(graphSetup['title'],[1.0,0.0,0.0],1.0,1.0) 
titleOb.setLocation(0.0,1.05,0.05)
##############################
#
mat = Material.New('xyBackMat') # create a new Material called 'newMat' 
mat.rgbCol = [1.0, 1.0, 1.0]      # change its color 
xyBackMe = Mesh.Primitives.Grid(2,2)
A = Matrix(
 [0.5,0,0,0],
 [0,0.5,0,0],
 [0,0,0.5,0],
 [0.5,0.5,-0.05,1])
xyBackMe.transform(A,True)
xyBackOb = scene.objects.new(xyBackMe,'xyBackMe')
xyBackOb.getData(False,True).materials += [mat]
# this makes the grid appear as an outline in the Blender editor
#xyBackOb.setDrawType(Object.DrawTypes["WIRE"])

#######################################
# render the image and save the image
#
context = scene.getRenderingContext() 
# enable seperate window for rendering 
Render.EnableDispWin() 
context.imageType = Render.JPEG
# draw the image 
context.render() 
# save the image to disk 
# to the location specified by RenderPath 
# by default this will be a jpg file 
context.saveRenderedImage('PlotExample02.jpg') 

Window.RedrawAll() 
#
########################################


To copy the code snippets easily, see:http://dataorigami.blogspot.com/2009/06/how-to-easily-copy-code-snippets-from.html

To execute the script in Blender, see:http://dataorigami.blogspot.com/2009/06/how-to-execute-script-from-command-line.html

To work with the script in a simple IDE, see:http://dataorigami.blogspot.com/2009/04/developing-scripts-for-blender.html