Friday, September 25, 2009
Data Source: Aggregated Data on commercial/retail information
Sunday, August 23, 2009
Release of Data Origami Library 0.006
In this release, the image class now supports the following methods:
- Orienting the camera by using a lookAt and lookFrom location
- Adding Blocks defined by opposing corners
- Adding Cylinders defined by end locations
- Adding Spheres
- Adding Text boxes
- Adding Lighting which can be aimed at a target
- Adding Tubes which can be used to draw arbitrary curves from either piecewise lines or smooth curves.
The library can be downloaded here. To install, simply unzip to a working directory and execute the test scripts from the Blender command line.
The images above were generated by the test scripts contained in the zip files.
This example shows how the library can be used to generate an image with just a few lines of python.
#BPY # setup to allow the module to be imported from the same directory # the script is in import sys import os filename = sys.argv[2] filepath = os.path.dirname(filename) sys.path.insert(0,filepath) import OLib as MyLib import Blender import Blender.Mathutils from Blender.Mathutils import * from Blender import * image = MyLib.Image('MyScene') #image.lookFrom(3.5,3,15) image.lookFrom(15,5,15) image.lookAt(0,0,1) ob = image.addPolyline() image.addTube(tubeShape=ob) image.addCylinder(p1=[0,0,0],p2=[0,0,5]) image.save(filepath+'\\images\\'+'test001_Tube.jpg')
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
Sunday, August 16, 2009
Script to find the type of each object in a Blender scene
import Blender from Blender import * scn = Scene.GetCurrent() obs = scn.objects for ob in obs: print 'Object named %s has type %s' % (ob.name,ob.type)
Monday, August 10, 2009
Portable Blender and Notepad++
"C:\PortableApps\BlenderPortable\blenderportable.exe" -P "$(FULL_CURRENT_PATH)"
Release of Data Origami Library 0.002
- Orienting the camera by using a lookAt and lookFrom location
- Blocks defined by opposing corners
- Cylinders defined by end locations
- Spheres
- Text boxes
An issue with shading was corrected. The library now generates shadows correctly.
The library can be downloaded here.
The image above was generated by the test script here.
Thursday, August 6, 2009
Interesting Dataset: BLS Time Use Survey
Sunday, August 2, 2009
The 0.001 Release of the Data Origami Library for Blender
For now, it is recommended to place this file in C:\tmp. I'll use that location in example scripts. A future release will have a more permanent recommended home.
Code: Download Code for the Origami Library 0.001. This file can be placed arbitrarily. Any script which imports this module must reference it by the full path because of how Blender handles files.
#!BPY __doc__ = """ OLib_xxxx.py Rev 0.001 This module setups up a scene for static rendering, animation or game engine operation This module provides useful library functions. This module does nothing from the Blender command line ------------------ History: rev 0.001 8/2/2009 Initial release to test structure and functionality edt """ __author__ = "edt" __version__ = "0.001 2009/08/03" __url__="Website, dataOrigami.blogspot.com" ############################################################## __doc__ = """ This module setups up a scene for static rendering, animation or game engine operation This module imports useful library functions. rev 0.001 Initial release to test structure and functionality edt 8/2/2009 """ import Blender import Blender.Mathutils from Blender.Mathutils import * class Image(object): def __init__(self,name='Scene'): # create new scene scene = Blender.Scene.New(name) scene.makeCurrent() ############################################################## # add a camera and set it up # camdata = Blender.Camera.New() cam = scene.objects.new(camdata) # use setLocation to control the position of the camera scene.objects.camera = cam self.cam = cam self._lookAt_x = 0 self._lookAt_y = 0 self._lookAt_z = 0 self.lookFrom(2.481,-1.508,1.1) ############################################################## # add a lamp and set it up # lampData = Blender.Lamp.New() lampData.setEnergy(1.0) lampData.setType('Lamp') lampData.mode |= Blender.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)) # ################################################################ # setup world world = Blender.World.New(name+'_World') world.setCurrent() self.scene = scene self.lamp = lamp self.world = world def lookFrom(self,*args): if len(args)==1: # we have an object which contains look information x = args[0].x y = args[0].y z = args[0].z elif len(args)==3: # we have x,y,z coordinates x = args[0] y = args[1] z = args[2] # set camera position to x,y,z self.cam.setLocation(x,y,z) self.lookAt() def lookAt(self,*args): if len(args)==0: to_x = self._lookAt_x to_y = self._lookAt_y to_z = self._lookAt_z up_x = 0 up_y = 1 up_z = 0 elif len(args)==1: # we have an object which contains look information to_x = args[0].x to_y = args[0].y to_z = args[0].z up_x = 0 up_y = 1 up_z = 0 elif len(args)==2: # we have an object which contains look information to_x = args[0].x to_y = args[0].y to_z = args[0].z up_x = args[1].x up_y = args[1].y up_z = args[1].z elif len(args)==3: # we have x,y,z coordinates to_x = args[0] to_y = args[1] to_z = args[2] up_x = 0 up_y = 1 up_z = 0 elif len(args)==6: # we have x,y,z coordinates to_x = args[0] to_y = args[1] to_z = args[2] up_x = args[3] up_y = args[4] up_z = args[5] # save view target self._lookAt_x = to_x self._lookAt_y = to_y self._lookAt_z = to_z # set camera position to x,y,z from_x, from_y, from_z = self.cam.getLocation() p1 = Vector([from_x,from_y,from_z]) p2 = Vector([to_x,to_y,to_z]) # form a vector that points in the direction from p1 to p2 dir = p2-p1 #print 'from='+str(p1) #print 'to='+str(p2) #print 'dir='+str(dir) dir.normalize() #u = dir up = Vector([up_x,up_y,up_z]) up.normalize() dir_ref = Vector([0,0,-1.0]) # direction the camera naturally points w/o rotation if True: #abs(AngleBetweenVecs(dir,dir_ref))>1e-6: d = -dir e = up-up*DotVecs(dir,up) e.normalize() f = -CrossVecs(d,e) A = Matrix( [f[0],f[1],f[2],0], [e[0],e[1],e[2],0], [d[0],d[1],d[2],0], [from_x,from_y,from_z,1]) else: # the direction of view is parallel to the z-axis A = Matrix( [1,0,0,0], [0,1,0,0], [0,0,1,0], [from_x,from_y,from_z,1]) # apply the transform to the camera self.cam.setMatrix(A) def add(self,item,name='noName'): # this method allows any object to be added to the scene ob = self.scene.objects.new(item,name) return ob def delete(self,item): self.scene.objects.unlink(item) def addSphere(self,**kwargs): # default the values diameter = kwargs.get('diameter',1.0) segments = kwargs.get('segments',8) rings = kwargs.get('rings',8) loc = kwargs.get('location',[0,0,0]) useIco = kwargs.get('useIco',False) useUV = kwargs.get('useUV',True) subdivisions = kwargs.get('subdivisions',2) name = kwargs.get('name','sphere') if useIco: sphere = Blender.Mesh.Primitives.Icosphere(subdivisions,diameter) else: sphere = Blender.Mesh.Primitives.UVsphere(segments,rings,diameter) ob = self.add(sphere,name) ob.setLocation(loc) return ob def addCube(self,*kwargs): pass def addCylinder(self,**kwargs): pass def addPlane(self,**kwargs): pass def addText(self,**kwargs): loc = kwargs.get('location',[0,0,0]) name = kwargs.get('name','sphere') txtStr= kwargs.get('text','Default Message') width = kwargs.get('width',1.0) height= kwargs.get('height',1.0) col = kwargs.get('color',[1.0,1.0,1.0]) align = kwargs.get('align','LEFT') txt = Blender.Text3d.New() txt.setText(txtStr) txt.setSize(0.1) alignDict = {'LEFT':Blender.Text3d.LEFT,'RIGHT':Blender.Text3d.RIGHT, 'MIDDLE':Blender.Text3d.MIDDLE, 'FLUSH':Blender.Text3d.FLUSH} txt.setAlignment(alignDict[align]) mat=Blender.Material.New('textMat') mat.rgbCol = col ob = self.add(txt) me = Blender.Mesh.New('textMesh') me.getFromObject(ob) me.materials += [mat] self.delete(ob) newOb = self.add(me,name) # force a redraw to ensure that the bounding box is updated!! Blender.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.setLocation(loc) return newOb def addWorld(self,**kwargs): pass def drawNow(self,**kwargs): xSize = kwargs.get('xSize',1024) ySize = kwargs.get('ySize',800) overSampleLevel = kwargs.get('oversamplingLevel',16) #print 'Image.drawNow() executing' self.context = self.scene.getRenderingContext() # enable seperate window for rendering Blender.Scene.Render.EnableDispWin() self.context.imageType = Blender.Scene.Render.JPEG if overSampleLevel>0: self.context.enableOversampling(True) self.context.setOversamplingLevel(overSampleLevel) else: self.context.enableOversampling(False) self.context.imageSizeX(xSize) self.context.imageSizeY(ySize) # draw the image self.context.render() def save(self,filename='image.jpg'): self.drawNow() self.context.saveRenderedImage(filename) class Animation(object): pass class BEG(object): pass # This does not work with Blender #if name=='__main__': # # test/demo codes here # pass
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
Using Blender Subsurfaces from Python
To get this example to run, you will need to copy the Data Origami Library rev 0.001 to C:\tmp (or modify the path to point to your location of choice).
Code:
Download Code to draw the spheres with different rings, segments and subsurface levels.
#BPY __doc__ = """ Sphere Subsurface Example Rev 1.0 This script demonstrates the effect of subsurface. This script depends on the Origami Library and expects it to be located at C:\tmp. This script is executed at the command line by:>blender -P subsurf_ex_XXXX.py""" __author__ = "edt" __version__ = "1.0 2009/08/03" __url__="Website, dataOrigami.blogspot.com" ############################################################## import sys # make sure a copy of OLib_001.py is at C:\tmp # add to the system path to point to the file to import sys.path.append("c:\\tmp") import OLib_001 as MyLib import Blender import Blender.Mathutils from Blender.Mathutils import * from Blender import * image = MyLib.Image('MyScene') image.lookFrom(3.5,3,15) image.lookAt(3.5,3,0) image.addText(text='UVSpheres with SubSurfaces',width=5,height=1, location=[5,6.5,0],align='MIDDLE') xList = range(0,10) ringList = [3,4,5,6,7,8,9,10,11,12] segmentList = ringList image.addText(text='Num Rings =',width=10,height=0.4, location=[-0.25,-0.75,0],align='RIGHT', color=[1.0,0.0,0.0]) ob=image.addText(text='Num SubSurfaces', width=20,height=0.4, location=[-1,3,0],align='MIDDLE', color=[0.0,1.0,0.0]) ob.setEuler(0,0,90*3.1415/180.0) for i in range(0,len(ringList)): rings = ringList[i] segments = segmentList[i] image.addText(text=str(rings),width=1,height=0.4, location=[xList[i],-0.75,0],align='MIDDLE', color=[1.0,0.0,0.0]) for subSurfLevel in range(0,7): # add a UVsphere UVSphereOb = image.addSphere(diameter=0.5,rings=rings, segments=segments,location=[xList[i],subSurfLevel,0]) mods = UVSphereOb.modifiers mod = mods.append(Modifier.Types.SUBSURF) # add a new subsurf modifier mod[Modifier.Settings.LEVELS] = subSurfLevel # set subsurf subdivision levels to 3 image.addText(text=str(subSurfLevel),width=1,height=0.4, location=[-0.5,subSurfLevel-0.2,0],align='RIGHT', color=[0.0,1.0,0.0]) image.save('SubSurfaceExample.jpg')
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
Thursday, July 30, 2009
Interesting Travel Dataset
Saturday, July 25, 2009
Interesting Dataset: Residential Energy Consumption Survey
Sunday, July 19, 2009
How to draw a curve in Blender using Python - A Quick-N-Dirty Example
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
#!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
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
- 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:
- Copy the data file from this example to the '\tmp' directory on the machine. If the '\tmp' directory does not exist, create it.
- 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.
- To execute the script, from the command line execute the following: "blender -P plotExample02.py"
#!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
Sunday, June 21, 2009
Quick & Dirty: Example of generating a graph in Blender
This is a quick posting. It demonstrates how to generate an x-y graph in Blender. Two functions are introduced so a curve can be generated from mesh primitives. The join method is used to combine the mesh primitives into a single object which is easy to edit and modify.
Code:
#!BPY __doc__ = """ SimplePlottingExample.py Example demonstrating the following techniques in Blender 2.48 to generate a plot of x-y data: 1) Joining meshes 2) Use of the transform matrix 3) Use of material properties to control how objects are rendered 4) Delete (unlink) of objects in a scene 5) Use of cross products to generate a basis for the transform matrix 6) Using python to render an image 7) Using python to save the rendered image to disk 8) Use of Blender Vector and Matrix classes 9) Defining a function in Python 10) Using Python to parse a file This script is executed at the command line by: >blender -P OrientationExample.py """ __author__ = "edt" __version__ = "1.0 2009/06/22" __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 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]) if abs(AngleBetweenVecs(u,uu))>1e-6: # 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 ############################################################## # Get rid of the lamp and cube from the default scene scene = Scene.GetCurrent() for ob in scene.objects: print ob.getName() if ((cmp(ob.getName(),'Lamp')==0) | (cmp(ob.getName(),'Cube')==0) | (cmp(ob.getName(),'Camera')==0)): scene.objects.unlink(ob) ############################################################## # 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') lamp = scene.objects.new(lampData) lamp.setLocation(-2.0,2.0,5.0) lamp.setEuler(120*(3.1415/180),30*(3.1415/180),0*(3.1415/180)) ############################################################## # load the data # f = open('c:\\tmp\\EmploymentPopRatio.txt', 'r') foundData = False xData = [] yData = [] zData = [] xLabels = [] 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]+'||' xData.append(float(year+(month-1)/12.0)) xLabels.append(str(year)+','+str(month)) yData.append(float(fields[i])) zData.append(0.05) print xData print yData # build point list pointList = [] for i in range(0,len(xData)): px = (xData[i]-1948.0)/50.0 py = yData[i]/100.0 pz = zData[i] point = Vector([px,py,pz]) pointList.append(point) print pointList ############################################################## # create the objects in the scene and bind materials to them # # form meshes for the line, join meshes so only one # object describes the curve meList = curve(pointList,[1.0,0.1,0.4],0.01,8) rooted = False for me in meList: if not rooted: ob = scene.objects.new(me,'curveOb') rooted = True else: localOb = scene.objects.new(me,'localCurveOb') ob.join([localOb]) scene.objects.unlink(localOb) # ############################## # # create the lines for axes and a ball at the origin p0 = Vector([0,0,0]) px = Vector([1,0,0]) py = Vector([0,1,0]) pz = Vector([0,0,1]) xMe = lineSegMe(p0,px,0.01,32) yMe = lineSegMe(p0,py,0.01,32) zMe = lineSegMe(p0,pz,0.01,32) xOb = scene.objects.new(xMe,'xAxisOb') yOb = scene.objects.new(yMe,'yAxisOb') zOb = scene.objects.new(zMe,'zAxisOb') originMe = Mesh.Primitives.UVsphere(32,32,0.01) originOb = scene.objects.new(originMe,'originOb') mat = Material.New('axisMat') # create a new Material called 'newMat' mat.rgbCol = [1.0, 1.0, 0.0] # change its color # attach the materials using two different techniques # >for the xaxis and the origin, the material is attached to the mesh by # by referenging the mesh from the object using # getData # > for the y & z-axis, we use the mesh reference. Since the # objects in the scene just point to the original mesh # attached to them, changing the mesh changes the object. originOb.getData(False,True).materials+=[mat] xOb.getData(False,True).materials += [mat] yMe.materials += [mat] zMe.materials += [mat] # create a grid for the axes and place it on the x-y plane with # use material properties to make it look like a grid # by only drawing vertices and edges mat = Material.New('axisGridMat') # create a new Material called 'newMat' mat.rgbCol = [1.0, 1.0, 0.0] # change its color mat.mode |= Material.Modes.WIRE # only render edges and vertices zGridMe = Mesh.Primitives.Grid(10,10,1) zGridOb = scene.objects.new(zGridMe,'zGridOb') zGridOb.setLocation(0.5,0.5,0) zGridOb.getData(False,True).materials += [mat] # this makes the grid appear as an outline in the Blender editor zGridOb.setDrawType(Object.DrawTypes["WIRE"]) 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.0,2.0) 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('PlotExampleQND.jpg') Window.RedrawAll() # ########################################
Data Files from Bureau Labor Statistics:
Series Id: LNS12300000 http://data.bls.gov/PDQ/servlet/SurveyOutputServlet?data_tool=latest_numbers&series_id=LNS12300000 Seasonal Adjusted Series title: (Seas) Employment-Population Ratio Labor force status: Employment-population ratio Type of data: Percent Age: 16 years and over Year,Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec,Annual, 1948,56.6,56.7,56.1,56.7,56.2,57.0,57.1,56.6,56.6,56.5,56.5,56.8, 1949,56.2,56.2,56.0,55.7,55.4,55.0,55.0,55.1,55.3,54.9,55.6,55.3, 1950,55.1,55.1,55.1,55.8,55.8,56.2,56.1,56.8,56.6,56.9,56.9,56.7, 1951,56.9,57.0,57.7,57.3,57.6,57.1,57.6,57.4,57.1,57.3,57.1,57.7, 1952,57.7,57.7,57.1,57.1,57.3,57.3,57.0,56.8,57.4,56.9,57.5,57.6, 1953,57.8,58.0,58.1,57.5,57.1,57.4,57.4,57.1,56.8,56.7,56.5,55.7, 1954,55.7,56.2,55.7,55.7,55.4,55.2,55.0,55.2,55.5,55.5,55.5,55.2, 1955,55.7,55.7,55.8,56.2,56.3,56.3,56.9,57.1,57.2,57.2,57.4,57.7, 1956,57.8,57.5,57.3,57.5,57.6,57.5,57.5,57.6,57.6,57.5,57.3,57.3, 1957,57.0,57.5,57.6,57.2,57.1,57.2,57.5,56.9,57.0,56.8,56.4,56.6, 1958,55.9,55.5,55.3,55.2,55.4,55.2,55.2,55.4,55.4,55.6,55.5,55.5, 1959,55.7,55.5,56.0,56.3,56.2,56.3,56.3,56.1,56.0,56.1,55.7,56.3, 1960,56.0,56.2,55.4,56.4,56.4,56.5,56.2,56.1,56.4,55.8,56.1,55.7, 1961,55.7,55.5,55.6,55.2,55.2,55.6,55.2,55.3,55.0,55.3,55.5,55.3, 1962,55.4,55.7,55.7,55.4,55.7,55.6,55.3,55.7,55.7,55.5,55.2,55.2, 1963,55.2,55.1,55.3,55.5,55.3,55.3,55.4,55.4,55.5,55.5,55.4,55.3, 1964,55.3,55.6,55.5,55.9,56.1,55.6,55.7,55.7,55.7,55.6,55.7,55.6, 1965,55.7,55.7,55.9,56.0,56.2,56.1,56.5,56.3,56.2,56.4,56.4,56.6, 1966,56.7,56.6,56.6,56.8,56.7,56.9,56.9,57.0,57.1,57.1,57.4,57.3, 1967,57.1,57.0,56.8,57.1,57.0,57.3,57.4,57.4,57.4,57.5,57.5,57.6, 1968,57.0,57.3,57.4,57.4,57.8,57.8,57.6,57.5,57.5,57.5,57.6,57.7, 1969,57.6,57.9,57.9,57.9,57.8,58.0,58.0,58.1,58.1,58.1,58.1,58.1, 1970,58.0,57.9,57.9,57.9,57.5,57.3,57.4,57.2,57.0,57.0,56.9,56.7, 1971,56.8,56.6,56.4,56.6,56.6,56.2,56.5,56.6,56.6,56.6,56.8,56.8, 1972,56.7,56.7,56.9,56.9,57.0,57.0,57.0,57.1,57.0,57.0,57.2,57.3, 1973,57.1,57.5,57.8,57.7,57.7,58.0,57.9,57.8,57.9,58.1,58.2,58.2, 1974,58.2,58.2,58.2,58.0,58.0,58.0,58.0,57.8,57.7,57.6,57.3,56.9, 1975,56.4,56.1,56.0,55.9,56.0,55.8,56.0,56.1,56.1,56.1,56.0,56.1, 1976,56.4,56.5,56.7,56.8,57.0,56.8,57.0,57.0,56.9,56.9,57.0,57.0, 1977,57.0,57.2,57.4,57.6,57.8,57.9,57.8,58.0,58.1,58.2,58.6,58.7, 1978,58.8,58.8,58.8,59.2,59.3,59.5,59.3,59.4,59.5,59.7,59.8,59.8, 1979,59.9,60.1,60.0,59.8,59.8,59.9,60.0,59.8,60.0,59.9,60.0,60.1, 1980,60.0,60.0,59.7,59.4,59.1,58.9,58.8,58.8,58.9,58.9,59.0,59.0, 1981,59.1,59.2,59.4,59.6,59.5,59.0,59.1,59.1,58.7,58.8,58.6,58.2, 1982,58.2,58.2,58.1,57.9,58.2,57.8,57.7,57.8,57.6,57.4,57.3,57.2, 1983,57.2,57.1,57.1,57.3,57.3,57.8,58.1,58.2,58.4,58.4,58.7,58.8, 1984,58.8,59.1,59.1,59.3,59.7,59.9,59.8,59.6,59.7,59.7,59.8,59.9, 1985,59.9,60.0,60.2,60.1,60.1,59.8,59.9,60.0,60.3,60.3,60.4,60.4, 1986,60.6,60.3,60.5,60.5,60.5,60.7,60.8,60.8,60.8,60.9,60.9,61.0, 1987,61.0,61.1,61.2,61.3,61.6,61.4,61.6,61.8,61.6,61.8,61.9,62.0, 1988,62.0,62.1,61.9,62.2,62.0,62.3,62.3,62.4,62.4,62.5,62.7,62.6, 1989,62.9,62.9,62.9,62.9,62.9,63.0,63.0,63.1,62.8,62.9,63.0,63.0, 1990,63.2,63.2,63.2,63.0,63.1,62.9,62.8,62.7,62.5,62.5,62.3,62.2, 1991,62.0,61.9,61.8,62.0,61.6,61.7,61.6,61.5,61.6,61.5,61.4,61.2, 1992,61.5,61.3,61.5,61.6,61.5,61.5,61.6,61.6,61.4,61.3,61.4,61.4, 1993,61.4,61.4,61.5,61.5,61.7,61.8,61.8,62.0,61.7,61.8,61.9,62.0, 1994,62.2,62.3,62.1,62.3,62.5,62.3,62.3,62.6,62.7,62.9,63.0,63.1, 1995,63.0,63.1,63.1,63.1,62.7,62.7,62.8,62.8,62.9,62.9,62.8,62.7, 1996,62.7,62.9,63.0,63.0,63.0,63.2,63.3,63.3,63.4,63.5,63.4,63.4, 1997,63.4,63.4,63.6,63.7,63.8,63.7,63.9,63.9,63.9,63.9,64.1,64.0, 1998,64.0,64.0,64.0,64.1,64.1,64.0,64.0,63.9,64.2,64.1,64.2,64.3, 1999,64.4,64.2,64.2,64.2,64.3,64.2,64.2,64.2,64.2,64.3,64.4,64.4, 2000,64.6,64.6,64.6,64.7,64.4,64.5,64.2,64.2,64.2,64.2,64.3,64.4, 2001,64.4,64.3,64.3,64.0,63.8,63.7,63.7,63.2,63.5,63.2,63.0,62.9, 2002,62.7,63.0,62.8,62.7,62.9,62.7,62.7,62.7,63.0,62.7,62.5,62.4, 2003,62.5,62.5,62.4,62.4,62.3,62.3,62.1,62.1,62.0,62.1,62.3,62.2, 2004,62.3,62.3,62.2,62.3,62.3,62.4,62.5,62.4,62.3,62.3,62.5,62.4, 2005,62.4,62.4,62.4,62.7,62.7,62.7,62.8,62.9,62.8,62.8,62.7,62.8, 2006,62.9,63.0,63.0,63.0,63.1,63.1,63.0,63.1,63.1,63.3,63.3,63.4, 2007,63.3,63.2,63.3,63.0,63.0,63.0,62.9,62.8,62.9,62.7,63.0,62.7, 2008,62.9,62.7,62.7,62.7,62.5,62.4,62.3,62.1,61.9,61.7,61.4,61.0, 2009,60.5,60.3,59.9,59.9,59.7, , , , , , , ,
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
Saturday, June 20, 2009
How to set the render path in Blender
To set the location where renderings are saved in Blender do the following steps:
First, either press F10 or use the menus to select Render>Render Settings
Once you've done this, the panel at the bottom of the screen should show a collection of panels where the render settings can be changed. Select the text box which specifies where to save the rendered images.
How to easily copy code snippets from the formatted code display
# code example print 'Hello World!"
You can easily copy the text by clicking on the button in the upper right of the code block that is looks like <>. A popup will present the code without HTML or any other extra information. You can copy this to a clip board and paste the code into your favorite ide.
How to execute a script from the command line in Blender
With Blender you can execute a Python script when Blender is launched by specifying the scipt:
>blender -P myscript.py
Blender tranform matrix and building scenes from primitive meshes
- the use of the transform matrix (mesh.transform()) to orient and place a mesh in a scene,
- the use of material properties to control how a mesh is rendered, and
- the use of Blender Vector and Matrix classes.
#!BPY __doc__ = """ OrientationExample.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 This script is executed at the command line by: >blender -P OrientationExample.py """ __author__ = "edt" __version__ = "1.0 2009/06/20" __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 lineMe(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]) if abs(AngleBetweenVecs(u,uu))>1e-6: # 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 ############################################################## # Get rid of the lamp and cube from the default scene scene = Scene.GetCurrent() for ob in scene.objects: print ob.getName() if ((cmp(ob.getName(),'Lamp')==0) (cmp(ob.getName(),'Cube')==0) (cmp(ob.getName(),'Camera')==0)): scene.objects.unlink(ob) ############################################################## # 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(2.481,-1.508,1.1) # use set Euler to control the angle of the camera cam.setEuler(73.559*(3.1415/180),0.620*(3.1415/180),46.692*(3.1415/180)) scene.objects.camera = cam ############################################################## # add a lamp and set it up # lampdata = Lamp.New() lampdata.setEnergy(1.0) lampdata.setType('Hemi') lamp = scene.objects.new(lampdata) lamp.setLocation(-5.0,-6.0,5.0) lamp.setEuler(0.0,0.0,0.0) ############################################################## # create the objects in the scene and bind materials to them # # define the ends of the line p1 = Vector([0.2,0.3,-0.1]) p2 = Vector([0.5,1.0,1.0]) # form a mesh for th eline me = lineMe(p1,p2) # add the line to the scene ob = scene.objects.new(me,'lineOb') # bind a red material to the line mat = Material.New('redMat') # create a new Material called 'redMat' mat.rgbCol = [0.8, 0.1, 0.5] # change the color of the line # # attach the material to the mesh # note 1) a different technique is used for meshes # compared to how materials are attached to objects # note 2) we can attach the material to the mesh even after # the mesh was assigned to the object. This happens # because the mesh and the object really are pointers # to the same information in memory. me.materials += [mat] # ############################## # # create the lines for axes and a ball at the origin p0 = Vector([0,0,0]) px = Vector([1,0,0]) py = Vector([0,1,0]) pz = Vector([0,0,1]) xMe = lineMe(p0,px) yMe = lineMe(p0,py) zMe = lineMe(p0,pz) xOb = scene.objects.new(xMe,'xAxisOb') yOb = scene.objects.new(yMe,'yAxisOb') zOb = scene.objects.new(zMe,'zAxisOb') originMe = Mesh.Primitives.UVsphere(32,32,0.1) originOb = scene.objects.new(originMe,'originOb') mat = Material.New('axisMat') # create a new Material called 'newMat' mat.rgbCol = [1.0, 1.0, 0.0] # change its color # attach the materials using two different techniques # >for the xaxis and the origin, the material is attached to the mesh by # by referenging the mesh from the object using # getData # > for the y & z-axis, we use the mesh reference. Since the # objects in the scene just point to the original mesh # attached to them, changing the mesh changes the object. originOb.getData(False,True).materials+=[mat] xOb.getData(False,True).materials += [mat] yMe.materials += [mat] zMe.materials += [mat] # create a grid for the axes and place it on the x-y plane with # use material properties to make it look like a grid # by only drawing vertices and edges mat = Material.New('axisGridMat') # create a new Material called 'newMat' mat.rgbCol = [1.0, 1.0, 0.0] # change its color mat.mode = Material.Modes.WIRE # only render edges and vertices zGridMe = Mesh.Primitives.Grid(10,10,1) zGridOb = scene.objects.new(zGridMe,'zGridOb') zGridOb.setLocation(0.5,0.5,0) zGridOb.getData(False,True).materials += [mat] # this makes the grid appear as an outline in the Blender editor zGridOb.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('LineOrientationExample.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