This commit was manufactured by cvs2svn to create branch 'orange'.
authorno-author <no-author>
Thu, 26 Jan 2006 22:47:31 +0000 (22:47 +0000)
committerno-author <no-author>
Thu, 26 Jan 2006 22:47:31 +0000 (22:47 +0000)
12 files changed:
release/scripts/archimap.py [new file with mode: 0644]
release/scripts/bpydata/mesh_bbrush.py [new file with mode: 0644]
release/scripts/bpymodules/boxpack2d.py [new file with mode: 0644]
release/scripts/image_edit.py [new file with mode: 0644]
release/scripts/mesh_bbrush_menu.py [new file with mode: 0644]
release/scripts/mesh_cleanup.py [new file with mode: 0644]
release/scripts/mesh_tri2quad.py [new file with mode: 0644]
release/scripts/object_batch_name_edit.py [new file with mode: 0644]
release/scripts/ply_export.py [new file with mode: 0644]
release/scripts/ply_import.py [new file with mode: 0644]
release/scripts/xfig_export.py [new file with mode: 0644]
source/blender/python/api2_2x/doc/Pose.py [new file with mode: 0644]

diff --git a/release/scripts/archimap.py b/release/scripts/archimap.py
new file mode 100644 (file)
index 0000000..b1881cf
--- /dev/null
@@ -0,0 +1,1138 @@
+#!BPY
+
+""" Registration info for Blender menus: <- these words are ignored
+Name: 'ArchiMap UV Projection Unwrapper'
+Blender: 240
+Group: 'UV'
+Tooltip: 'ArchiMap UV Unwrap mesh faces for all select mesh objects'
+"""
+
+
+__author__ = "Campbell Barton"
+__url__ = ("blender", "elysiun")
+__version__ = "1.1 12/18/05"
+
+__bpydoc__ = """\
+This script projection unwraps the selected faces of a mesh.
+
+it operates on all selected mesh objects, and can be set to unwrap
+selected faces, or all faces.
+"""
+
+# -------------------------------------------------------------------------- 
+# Archimap UV Projection Unwrapper v1.1 by Campbell Barton (AKA Ideasman) 
+# -------------------------------------------------------------------------- 
+# ***** BEGIN GPL LICENSE BLOCK ***** 
+# 
+# This program is free software; you can redistribute it and/or 
+# modify it under the terms of the GNU General Public License 
+# as published by the Free Software Foundation; either version 2 
+# of the License, or (at your option) any later version. 
+# 
+# This program is distributed in the hope that it will be useful, 
+# but WITHOUT ANY WARRANTY; without even the implied warranty of 
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
+# GNU General Public License for more details. 
+# 
+# You should have received a copy of the GNU General Public License 
+# along with this program; if not, write to the Free Software Foundation, 
+# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
+# 
+# ***** END GPL LICENCE BLOCK ***** 
+# -------------------------------------------------------------------------- 
+
+
+from Blender import Object, Scene, Draw, Window, sys, Mesh
+from Blender.Mathutils import CrossVecs, Matrix, Vector, RotationMatrix, DotVecs, TriangleArea
+
+from math import cos
+
+DEG_TO_RAD = 0.017453292519943295 # pi/180.0
+SMALL_NUM = 0.000000001
+BIG_NUM = 1e15
+
+global USER_FILL_HOLES
+global USER_FILL_HOLES_QUALITY
+USER_FILL_HOLES = None
+USER_FILL_HOLES_QUALITY = None
+
+import boxpack2d
+# reload(boxpack2d) # for developing.
+
+
+# Do 2 lines intersect?
+def lineIntersection2D(x1,y1, x2,y2, _x1,_y1, _x2,_y2):
+       
+       # Bounding box intersection first.
+       if min(x1, x2) > max(_x1, _x2) or \
+       max(x1, x2) < min(_x1, _x2) or \
+       min(y1, y2) > max(_y1, _y2) or \
+       max(y1, y2) < min(_y1, _y2):
+               return None, None # BAsic Bounds intersection TEST returns false.
+       
+       # are either of the segments points? Check Seg1
+       if abs(x1 - x2) + abs(y1 - y2) <= SMALL_NUM:
+               return None, None
+       
+       # are either of the segments points? Check Seg2
+       if abs(_x1 - _x2) + abs(_y1 - _y2) <= SMALL_NUM:
+               return None, None
+       
+       
+       # Make sure the HOZ/Vert Line Comes first.
+       if abs(_x1 - _x2) < SMALL_NUM or abs(_y1 - _y2) < SMALL_NUM:
+               x1, x2, y1, y2, _x1, _x2, _y1, _y2 = _x1, _x2, _y1, _y2, x1, x2, y1, y2
+
+
+       if abs(x2-x1) < SMALL_NUM: # VERTICLE LINE
+               if abs(_x2-_x1) < SMALL_NUM: # VERTICLE LINE SEG2
+                       return None, None # 2 verticle lines dont intersect.
+               
+               elif abs(_y2-_y1) < SMALL_NUM:
+                       return x1, _y1 # X of vert, Y of hoz. no calculation.           
+               
+               yi = ((_y1 / abs(_x1 - _x2)) * abs(_x2 - x1)) + ((_y2 / abs(_x1 - _x2)) * abs(_x1 - x1))
+               
+               if yi > max(y1, y2): # New point above seg1's vert line
+                       return None, None
+               elif yi < min(y1, y2): # New point below seg1's vert line
+                       return None, None
+                       
+               return x1, yi # Intersecting.
+       
+       if abs(y2-y1) < SMALL_NUM: # HOZ LINE
+               if abs(_y2-_y1) < SMALL_NUM: # HOZ LINE SEG2
+                       return None, None # 2 hoz lines dont intersect.
+               
+               # Can skip vert line check for seg 2 since its covered above.   
+               xi = ((_x1 / abs(_y1 - _y2)) * abs(_y2 - y1)) + ((_x2 / abs(_y1 - _y2)) * abs(_y1 - y1))
+               if xi > max(x1, x2): # New point right of seg1's hoz line
+                       return None, None
+               elif xi < min(x1, x2): # New point left of seg1's hoz line
+                       return None, None
+                       
+               return xi, y1 # Intersecting.
+               
+       
+       # ACCOUNTED FOR HOZ/VERT LINES. GO ON WITH BOTH ANGLULAR.
+       
+       b1 = (y2-y1)/(x2-x1)
+       b2 = (_y2-_y1)/(_x2-_x1)
+       a1 = y1-b1*x1
+       a2 = _y1-b2*_x1
+       
+       if b1 - b2 == 0.0:
+               return None, None       
+       
+       xi = - (a1-a2)/(b1-b2)
+       yi = a1+b1*xi
+       if (x1-xi)*(xi-x2) >= 0 and (_x1-xi)*(xi-_x2) >= 0 and (y1-yi)*(yi-y2) >= 0 and (_y1-yi)*(yi-_y2)>=0:
+               return xi, yi
+       else:
+               return None, None
+
+dict_matrix = {}
+
+def pointInTri2D(v, v1, v2, v3):
+       global dict_matrix
+       
+       key = (v1.x, v1.y, v2.x, v2.y, v3.x, v3.y)
+       
+       try:
+               mtx = dict_matrix[key]
+               if not mtx:
+                       return False
+       except:
+               side1 = v2 - v1
+               side2 = v3 - v1
+               
+               nor = CrossVecs(side1, side2)
+               
+               l1 = [side1[0], side1[1], side1[2]]
+               l2 = [side2[0], side2[1], side2[2]]
+               l3 = [nor[0], nor[1], nor[2]]
+               
+               mtx = Matrix(l1, l2, l3)
+               
+               # Zero area 2d tri, even tho we throw away zerop area faces
+               # the projection UV can result in a zero area UV.
+               if not mtx.determinant():
+                       dict_matrix[key] = None
+                       return False
+               
+               mtx.invert()
+               
+               dict_matrix[key] = mtx
+       
+       uvw = (v - v1) * mtx
+       return 0 <= uvw[0] and 0 <= uvw[1] and uvw[0] + uvw[1] <= 1
+
+
+def faceArea(f):
+       if len(f.v) == 3:
+               return TriangleArea(f.v[0].co, f.v[1].co, f.v[2].co)
+       elif len(f.v) == 4:
+               return\
+                TriangleArea(f.v[0].co, f.v[1].co, f.v[2].co) +\
+                TriangleArea(f.v[0].co, f.v[2].co, f.v[3].co)
+
+       
+def boundsIsland(faces):
+       minx = maxx = faces[0].uv[0][0] # Set initial bounds.
+       miny = maxy = faces[0].uv[0][1]
+       # print len(faces), minx, maxx, miny , maxy
+       for f in faces:
+               for uv in f.uv:
+                       minx = min(minx, uv[0])
+                       maxx = max(maxx, uv[0])
+                       
+                       miny = min(miny, uv[1])
+                       maxy = max(maxy, uv[1])
+       
+       return minx, miny, maxx, maxy
+
+def boundsEdgeLoop(edges):
+       minx = maxx = edges[0][0] # Set initial bounds.
+       miny = maxy = edges[0][1]
+       # print len(faces), minx, maxx, miny , maxy
+       for ed in edges:
+               for pt in ed:
+                       minx = min(minx, pt[0])
+                       maxx = max(maxx, pt[0])
+                       
+                       miny = min(miny, pt[1])
+                       maxy = max(maxy, pt[1])
+       
+       return minx, miny, maxx, maxy
+
+
+# Turns the islands into a list of unpordered edges (Non internal)
+# Onlt for UV's
+
+def island2Edge(island):
+       # Vert index edges
+       edges = {}
+       
+       for f in island:
+               for vIdx in range(len(f.v)):
+                       if f.v[vIdx].index > f.v[vIdx-1].index:
+                               edges[((f.uv[vIdx-1][0], f.uv[vIdx-1][1]), (f.uv[vIdx][0], f.uv[vIdx][1]))] =\
+                               (Vector([f.uv[vIdx-1][0], f.uv[vIdx-1][1]]) - Vector([f.uv[vIdx][0], f.uv[vIdx][1]])).length
+                       else:
+                               edges[((f.uv[vIdx][0], f.uv[vIdx][1]), (f.uv[vIdx-1][0], f.uv[vIdx-1][1]) )] =\
+                               (Vector([f.uv[vIdx-1][0], f.uv[vIdx-1][1]]) - Vector([f.uv[vIdx][0], f.uv[vIdx][1]])).length
+       
+       # If 2 are the same then they will be together, but full [a,b] order is not correct.
+       
+       # Sort by length
+       length_sorted_edges = []
+       for key in edges.keys():
+               length_sorted_edges.append([key[0], key[1], edges[key]])
+       
+       length_sorted_edges.sort(lambda A, B: cmp(B[2], A[2]))
+       #for e in length_sorted_edges:
+       #       e.pop(2)
+       
+       return length_sorted_edges
+       
+# ========================= NOT WORKING????
+# Find if a points inside an edge loop, un-orderd.
+# pt is and x/y
+# edges are a non ordered loop of edges.
+# #offsets are the edge x and y offset.
+"""
+def pointInEdges(pt, edges):
+       #
+       x1 = pt[0] 
+       y1 = pt[1]
+       
+       # Point to the left of this line.
+       x2 = -100000
+       y2 = -10000
+       intersectCount = 0
+       for ed in edges:
+               xi, yi = lineIntersection2D(x1,y1, x2,y2, ed[0][0], ed[0][1], ed[1][0], ed[1][1])
+               if xi != None: # Is there an intersection.
+                       intersectCount+=1
+       
+       return intersectCount % 2
+"""
+
+def uniqueEdgePairPoints(edges):
+       points = {}
+       pointsVec = []
+       for e in edges:
+               points[e[0]] = points[e[1]] = None
+               
+       for p in points.keys():
+               pointsVec.append( Vector([p[0], p[1], 0])  )
+       return pointsVec
+       
+
+def pointInIsland(pt, island):
+       vec1 = Vector(); vec2 = Vector(); vec3 = Vector()       
+       for f in island:
+               vec1.x, vec1.y = f.uv[0]
+               vec2.x, vec2.y = f.uv[1]
+               vec3.x, vec3.y = f.uv[2]
+
+               if pointInTri2D(pt, vec1, vec2, vec3):
+                       return True
+               
+               if len(f.v) == 4:
+                       vec1.x, vec1.y = f.uv[0]
+                       vec2.x, vec2.y = f.uv[2]
+                       vec3.x, vec3.y = f.uv[3]                        
+                       if pointInTri2D(pt, vec1, vec2, vec3):
+                               return True
+       return False
+
+
+# box is (left,bottom, right, top)
+def islandIntersectUvIsland(source, target, xSourceOffset, ySourceOffset):
+       # Is 1 point in the box, inside the vertLoops
+       edgeLoopsSource = source[6] # Pretend this is offset
+       edgeLoopsTarget = target[6]
+       
+       # Edge intersect test   
+       for ed in edgeLoopsSource:
+               for seg in edgeLoopsTarget:
+                       xi, yi = lineIntersection2D(\
+                       seg[0][0], seg[0][1], seg[1][0], seg[1][1],\
+                       xSourceOffset+ed[0][0], ySourceOffset+ed[0][1], xSourceOffset+ed[1][0], ySourceOffset+ed[1][1])
+                       if xi != None:
+                               return 1 # LINE INTERSECTION
+       
+       # 1 test for source being totally inside target
+       for pv in source[7]:
+               p = Vector(pv)
+               
+               p.x += xSourceOffset
+               p.y += ySourceOffset            
+               if pointInIsland(p, target[0]):
+                       return 2 # SOURCE INSIDE TARGET
+       
+       # 2 test for a part of the target being totaly inside the source.
+       for pv in target[7]:
+               p = Vector(pv)
+               
+               p.x -= xSourceOffset
+               p.y -= ySourceOffset
+               if pointInIsland(p, source[0]):
+                       return 3 # PART OF TARGET INSIDE SOURCE.
+
+       return 0 # NO INTERSECTION
+
+
+
+
+# Returns the X/y Bounds of a list of vectors.
+def testNewVecLs2DRotIsBetter(vecs, mat=-1, bestAreaSoFar = -1):
+       
+       # UV's will never extend this far.
+       minx = miny = BIG_NUM
+       maxx = maxy = -BIG_NUM
+       
+       for i, v in enumerate(vecs):
+               
+               # Do this allong the way
+               if mat != -1:
+                       v = vecs[i] = v*mat
+               
+               minx = min(minx, v.x)
+               maxx = max(maxx, v.x)
+               
+               miny = min(miny, v.y)
+               maxy = max(maxy, v.y)
+               
+               # Spesific to this algo, bail out if we get bigger then the current area
+               if bestAreaSoFar != -1 and (maxx-minx) * (maxy-miny) > bestAreaSoFar:
+                       return (BIG_NUM, None), None
+       w = maxx-minx
+       h = maxy-miny
+       return (w*h, w,h), vecs # Area, vecs
+       
+# Takes a list of faces that make up a UV island and rotate
+# until they optimally fit inside a square.
+ROTMAT_2D_POS_90D = RotationMatrix( 90, 2)
+ROTMAT_2D_POS_45D = RotationMatrix( 45, 2)
+
+RotMatStepRotation = []
+rot_angle = 22.5 #45.0/2
+while rot_angle > 0.1:
+       RotMatStepRotation.append([\
+        RotationMatrix( rot_angle, 2),\
+        RotationMatrix( -rot_angle, 2)])
+       
+       rot_angle = rot_angle/2.0
+       
+
+def optiRotateUvIsland(faces):
+       global currentArea
+       
+       # Bestfit Rotation
+       def best2dRotation(uvVecs, MAT1, MAT2):
+               global currentArea
+               
+               newAreaPos, newfaceProjectionGroupListPos =\
+               testNewVecLs2DRotIsBetter(uvVecs[:], MAT1, currentArea[0])
+               
+               
+               # Why do I use newpos here? May as well give the best area to date for an early bailout
+               # some slight speed increase in this.
+               # If the new rotation is smaller then the existing, we can 
+               # avoid copying a list and overwrite the old, crappy one.
+               
+               if newAreaPos[0] < currentArea[0]:
+                       newAreaNeg, newfaceProjectionGroupListNeg =\
+                       testNewVecLs2DRotIsBetter(uvVecs, MAT2, newAreaPos[0])  # Reuse the old bigger list.
+               else:
+                       newAreaNeg, newfaceProjectionGroupListNeg =\
+                       testNewVecLs2DRotIsBetter(uvVecs[:], MAT2, currentArea[0])  # Cant reuse, make a copy.
+               
+               
+               # Now from the 3 options we need to discover which to use
+               # we have cerrentArea/newAreaPos/newAreaNeg
+               bestArea = min(currentArea[0], newAreaPos[0], newAreaNeg[0])
+               
+               if currentArea[0] == bestArea:
+                       return uvVecs
+               elif newAreaPos[0] == bestArea:
+                       uvVecs = newfaceProjectionGroupListPos
+                       currentArea = newAreaPos                
+               elif newAreaNeg[0] == bestArea:
+                       uvVecs = newfaceProjectionGroupListNeg
+                       currentArea = newAreaNeg
+               
+               return uvVecs
+               
+       
+       # Serialized UV coords to Vectors
+       uvVecs = [Vector(uv) for f in faces  for uv in f.uv]
+       
+       # Theres a small enough number of these to hard code it
+       # rather then a loop.
+       
+       # Will not modify anything
+       currentArea, dummy =\
+       testNewVecLs2DRotIsBetter(uvVecs)
+       
+       
+       # Try a 45d rotation
+       newAreaPos, newfaceProjectionGroupListPos = testNewVecLs2DRotIsBetter(uvVecs[:], ROTMAT_2D_POS_45D, currentArea[0])
+       
+       if newAreaPos[0] < currentArea[0]:
+               uvVecs = newfaceProjectionGroupListPos
+               currentArea = newAreaPos
+       # 45d done
+       
+       # Testcase different rotations and find the onfe that best fits in a square
+       for ROTMAT in RotMatStepRotation:
+               uvVecs = best2dRotation(uvVecs, ROTMAT[0], ROTMAT[1])
+       
+       
+       # Only if you want it, make faces verticle!
+       if currentArea[1] > currentArea[2]:
+               # Rotate 90d
+               # Work directly on the list, no need to return a value.
+               testNewVecLs2DRotIsBetter(uvVecs, ROTMAT_2D_POS_90D)
+               
+       
+       
+       
+       # Now write the vectors back to the face UV's
+       i = 0 # count the serialized uv/vectors
+       for f in faces:
+               f.uv = [uv for uv in uvVecs[i:len(f.v)+i] ]
+               i += len(f.v)
+
+
+# Takes an island list and tries to find concave, hollow areas to pack smaller islands into.
+def mergeUvIslands(islandList, islandListArea):
+       global USER_FILL_HOLES
+       global USER_FILL_HOLES_QUALITY
+       
+       # Pack islands to bottom LHS
+       # Sync with island
+       
+       #islandTotFaceArea = [] # A list of floats, each island area
+       #islandArea = [] # a list of tuples ( area, w,h)
+       
+       
+       decoratedIslandList = []
+       
+       islandIdx = len(islandList)
+       while islandIdx:
+               islandIdx-=1
+               minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx])
+               w, h = maxx-minx, maxy-miny
+               
+               totFaceArea = 0
+               fIdx = len(islandList[islandIdx])
+               while fIdx:
+                       fIdx-=1
+                       f = islandList[islandIdx][fIdx]
+                       f.uv = [Vector(uv[0]-minx, uv[1]-miny) for uv in f.uv]
+                       totFaceArea += islandListArea[islandIdx][fIdx] # Use Cached area. dont recalculate.
+               islandBoundsArea = w*h
+               efficiency = abs(islandBoundsArea - totFaceArea)
+               
+               # UV Edge list used for intersections
+               edges = island2Edge(islandList[islandIdx])
+               
+               
+               uniqueEdgePoints = uniqueEdgePairPoints(edges)
+               
+               decoratedIslandList.append([islandList[islandIdx], totFaceArea, efficiency, islandBoundsArea, w,h, edges, uniqueEdgePoints]) 
+               
+       
+       # Sort by island bounding box area, smallest face area first.
+       # no.. chance that to most simple edge loop first.
+       decoratedIslandListAreaSort =decoratedIslandList[:]
+       decoratedIslandListAreaSort.sort(lambda A, B: cmp(A[1], B[1]))
+       
+       # sort by efficiency, Most Efficient first.
+       decoratedIslandListEfficSort = decoratedIslandList[:]
+       decoratedIslandListEfficSort.sort(lambda A, B: cmp(B[2], A[2]))
+       
+       # ================================================== THESE CAN BE TWEAKED.
+       # This is a quality value for the number of tests.
+       # from 1 to 4, generic quality value is from 1 to 100
+       USER_STEP_QUALITY =   ((USER_FILL_HOLES_QUALITY - 1) / 25.0) + 1
+       
+       # If 100 will test as long as there is enough free space.
+       # this is rarely enough, and testing takes a while, so lower quality speeds this up.
+       
+       # 1 means they have the same quaklity 
+       USER_FREE_SPACE_TO_TEST_QUALITY = 1 + (((100 - USER_FILL_HOLES_QUALITY)/100.0) *5)
+       
+       #print 'USER_STEP_QUALITY', USER_STEP_QUALITY
+       #print 'USER_FREE_SPACE_TO_TEST_QUALITY', USER_FREE_SPACE_TO_TEST_QUALITY
+       
+       removedCount = 0
+       
+       areaIslandIdx = 0
+       ctrl = Window.Qual.CTRL
+       while areaIslandIdx < len(decoratedIslandListAreaSort) and (not Window.GetKeyQualifiers() & ctrl):
+               sourceIsland = decoratedIslandListAreaSort[areaIslandIdx]
+               
+               # Alredy packed?
+               if not sourceIsland[0]:
+                       areaIslandIdx+=1
+               else:
+                       efficIslandIdx = 0
+                       while efficIslandIdx < len(decoratedIslandListEfficSort):
+                               
+                               # Now we have 2 islands, is the efficience of the islands lowers theres an
+                               # increasing likely hood that we can fit merge into the bigger UV island.
+                               # this ensures a tight fit.
+                               
+                               # Just use figures we have about user/unused area to see if they might fit.
+                               
+                               targetIsland = decoratedIslandListEfficSort[efficIslandIdx]
+                               
+                               
+                               if sourceIsland[0] == targetIsland[0] or\
+                               targetIsland[0] == [] or\
+                               sourceIsland[0] == []:
+                                       efficIslandIdx+=1
+                                       continue
+                               
+                               
+                               # ([island, totFaceArea, efficiency, islandArea, w,h])
+                               # Waisted space on target is greater then UV bounding island area.
+                               
+                               
+                               # if targetIsland[3] > (sourceIsland[2]) and\ #
+                               
+                               if targetIsland[3] > (sourceIsland[1] * USER_FREE_SPACE_TO_TEST_QUALITY) and\
+                               targetIsland[4] > sourceIsland[4] and\
+                               targetIsland[5] > sourceIsland[5]:
+                                       
+                                       
+                                       # DEBUG # print '%.10f  %.10f' % (targetIsland[3], sourceIsland[1])
+                                       
+                                       # These enough spare space lets move the box until it fits
+                                       
+                                       # How many times does the source fit into the target x/y
+                                       blockTestXUnit = targetIsland[4]/sourceIsland[4]
+                                       blockTestYUnit = targetIsland[5]/sourceIsland[5]
+                                       
+                                       boxLeft = 0
+                                       
+                                       # Distance we can move between whilst staying inside the targets bounds.
+                                       testWidth = targetIsland[4] - sourceIsland[4]
+                                       testHeight = targetIsland[5] - sourceIsland[5]
+                                       
+                                       # Increment we move each test. x/y
+                                       xIncrement = (testWidth / (blockTestXUnit * USER_STEP_QUALITY))
+                                       yIncrement = (testHeight / (blockTestYUnit * USER_STEP_QUALITY))
+                                       
+                                       boxLeft = 0 # Start 1 back so we can jump into the loop.
+                                       boxBottom= 0 #-yIncrement
+                                       
+                                       while boxLeft <= testWidth or boxBottom <= testHeight:
+                                               
+                                               
+                                               Intersect = islandIntersectUvIsland(sourceIsland, targetIsland, boxLeft, boxBottom)
+                                               
+                                               if Intersect == 1:  # Line intersect, dont bother with this any more
+                                                       pass
+                                               
+                                               if Intersect == 2:  # Source inside target
+                                                       '''
+                                                       We have an intersection, if we are inside the target 
+                                                       then move us 1 whole width accross,
+                                                       Its possible this is a bad idea since 2 skinny Angular faces
+                                                       could join without 1 whole move, but its a lot more optimal to speed this up
+                                                       since we have alredy tested for it.
+                                                       
+                                                       It gives about 10% speedup with minimal errors.
+                                                       '''
+                                                       # Move the test allong its width + SMALL_NUM
+                                                       boxLeft += sourceIsland[4] + SMALL_NUM
+                                               elif Intersect == 0: # No intersection?? Place it.
+                                                       # Progress
+                                                       removedCount +=1
+                                                       Window.DrawProgressBar(0.0, 'Merged: %i islands, Ctrl to finish early.' % removedCount)
+                                                       
+                                                       # Move faces into new island and offset
+                                                       targetIsland[0].extend(sourceIsland[0])
+                                                       while sourceIsland[0]:
+                                                               f = sourceIsland[0].pop()
+                                                               f.uv = [Vector(uv[0]+boxLeft, uv[1]+boxBottom) for uv in f.uv]
+
+                                                       # Move edge loop into new and offset.
+                                                       # targetIsland[6].extend(sourceIsland[6])
+                                                       while sourceIsland[6]:
+                                                               e = sourceIsland[6].pop()
+                                                               targetIsland[6].append(\
+                                                                ((e[0][0]+boxLeft, e[0][1]+boxBottom),\
+                                                                (e[1][0]+boxLeft, e[1][1]+boxBottom), e[2])\
+                                                               )
+                                                       
+                                                       # Sort by edge length, reverse so biggest are first.
+                                                       targetIsland[6].sort(lambda B,A: cmp(A[2], B[2] ))
+                                                       
+                                                       targetIsland[7].extend(sourceIsland[7])
+                                                       while sourceIsland[7]:
+                                                               p = sourceIsland[7].pop()
+                                                               p.x += boxLeft; p.y += boxBottom
+                                                       
+                                                       
+                                                       # Decrement the efficiency
+                                                       targetIsland[1]+=sourceIsland[1] # Increment totFaceArea
+                                                       targetIsland[2]-=sourceIsland[1] # Decrement efficiency
+                                                       # IF we ever used these again, should set to 0, eg
+                                                       sourceIsland[2] = 0 # No area is anyone wants to know
+                                                       
+                                                       break
+                                               
+                                               
+                                               # INCREMENR NEXT LOCATION
+                                               if boxLeft > testWidth:
+                                                       boxBottom += yIncrement
+                                                       boxLeft = 0.0
+                                               else:
+                                                       boxLeft += xIncrement
+                                               
+                                                       
+                               efficIslandIdx+=1
+               areaIslandIdx+=1
+       
+       # Remove empty islands
+       i = len(islandList)
+       while i:
+               i-=1
+               if not islandList[i]:
+                       islandList.pop(i) # Can increment islands removed here.
+       
+       
+# Takes groups of faces. assumes face groups are UV groups.
+def packLinkedUvs(faceGroups, faceGroupsArea, me):
+       islandList = []
+       islandListArea = []
+       
+       Window.DrawProgressBar(0.0, 'Splitting %d projection groups into UV islands:' % len(faceGroups))
+       #print '\tSplitting %d projection groups into UV islands:' % len(faceGroups),
+       # Find grouped faces
+       
+       faceGroupIdx = len(faceGroups)
+       
+       while faceGroupIdx:
+               faceGroupIdx-=1
+               faces = faceGroups[faceGroupIdx]
+               facesArea = faceGroupsArea[faceGroupIdx]
+               # print '.',
+               
+               faceUsers = [[] for i in xrange(len(me.verts)) ]
+               faceUsersArea = [[] for i in xrange(len(me.verts)) ]
+               # Do the first face
+               fIdx = len(faces)
+               while fIdx:
+                       fIdx-=1
+                       for v in faces[fIdx].v:
+                               faceUsers[v.index].append(faces[fIdx])
+                               faceUsersArea[v.index].append(facesArea[fIdx])
+                               
+               
+               while 1:                        
+                       
+                       # This is an index that is used to remember
+                       # what was the last face that was removed, so we know which faces are new and need to have 
+                       # faces next to them added into the list
+                       searchFaceIndex = 0
+                       
+                       # Find a face that hasnt been used alredy to start the search with
+                       newIsland = []
+                       newIslandArea = []
+                       while not newIsland:
+                               hasBeenUsed = 1 # Assume its been used.
+                               if searchFaceIndex >= len(faces):
+                                       break
+                               for v in faces[searchFaceIndex].v:
+                                       if faces[searchFaceIndex] in faceUsers[v.index]:
+                                               # This has not yet been used, it still being used by a vert
+                                               hasBeenUsed = 0
+                                               break
+                               if hasBeenUsed == 0:
+                                       newIsland.append(faces.pop(searchFaceIndex))
+                                       newIslandArea.append(facesArea.pop(searchFaceIndex))
+                               
+                               searchFaceIndex+=1
+
+                       if newIsland == []:
+                               break
+                       
+                       
+                       # Before we start remove the first, search face from being used.
+                       for v in newIsland[0].v:
+                               popoffset = 0
+                               for fIdx in xrange(len(faceUsers[v.index])):
+                                       if faceUsers[v.index][fIdx - popoffset] is newIsland[0]:                                                
+                                               faceUsers[v.index].pop(fIdx - popoffset)
+                                               faceUsersArea[v.index].pop(fIdx - popoffset)
+                                               
+                                               popoffset += 1
+                       
+                       searchFaceIndex = 0
+                       while searchFaceIndex != len(newIsland):
+                               for v in newIsland[searchFaceIndex].v:
+                                       
+                                       # Loop through all faces that use this vert
+                                       while faceUsers[v.index]:
+                                               sharedFace = faceUsers[v.index][-1]
+                                               sharedFaceArea = faceUsersArea[v.index][-1]
+                                               
+                                               newIsland.append(sharedFace)
+                                               newIslandArea.append(sharedFaceArea)
+                                               # Before we start remove the first, search face from being used.
+                                               for vv in sharedFace.v:
+                                                       #faceUsers = [f for f in faceUsers[vv.index] if f != sharedFace]
+                                                       fIdx = 0
+                                                       for fIdx in xrange(len(faceUsers[vv.index])):
+                                                               if faceUsers[vv.index][fIdx] is sharedFace:
+                                                                       faceUsers[vv.index].pop(fIdx)
+                                                                       faceUsersArea[vv.index].pop(fIdx)
+                                                                       break # Can only be used once.
+                               
+                               searchFaceIndex += 1
+                               
+                               # If all the faces are done and no face has been added then we can quit
+                       if newIsland:
+                               islandList.append(newIsland)
+                               
+                               islandListArea.append(newIslandArea)
+                       
+                       else:
+                               print '\t(empty island found, ignoring)'
+       
+       
+       Window.DrawProgressBar(0.1, 'Optimizing Rotation for %i UV Islands' % len(islandList))
+       
+       for island in islandList:
+               optiRotateUvIsland(island)
+       
+       
+       
+       if USER_FILL_HOLES:
+               Window.DrawProgressBar(0.1, 'Merging Islands (Ctrl: skip merge)...')
+               mergeUvIslands(islandList, islandListArea) # Modify in place
+               
+       
+       # Now we have UV islands, we need to pack them.
+       
+       # Make a synchronised list with the islands
+       # so we can box pak the islands.
+       boxes2Pack = []
+       
+       # Keep a list of X/Y offset so we can save time by writing the 
+       # uv's and packed data in one pass.
+       islandOffsetList = [] 
+       
+       islandIdx = 0
+       
+       while islandIdx < len(islandList):
+               minx, miny, maxx, maxy = boundsIsland(islandList[islandIdx])
+               w, h = maxx-minx, maxy-miny
+               
+               if w < 0.00001 or h < 0.00001:
+                       del islandList[islandIdx]
+                       islandIdx -=1
+                       continue
+               
+               '''Save the offset to be applied later,
+               we could apply to the UVs now and allign them to the bottom left hand area
+               of the UV coords like the box packer imagines they are
+               but, its quicker just to remember their offset and
+               apply the packing and offset in 1 pass '''
+               islandOffsetList.append((minx, miny))
+               
+               # Add to boxList. use the island idx for the BOX id.
+               boxes2Pack.append([islandIdx, w,h])
+               islandIdx+=1
+               
+       # Now we have a list of boxes to pack that syncs
+       # with the islands.
+       
+       #print '\tPacking UV Islands...'
+       Window.DrawProgressBar(0.7, 'Packing %i UV Islands...' % len(boxes2Pack) )
+       
+       time1 = sys.time()
+       packWidth, packHeight, packedLs = boxpack2d.boxPackIter(boxes2Pack)
+       # print 'Box Packing Time:', sys.time() - time1
+       
+       #if len(pa      ckedLs) != len(islandList):
+       #       raise "Error packed boxes differes from original length"
+       
+       #print '\tWriting Packed Data to faces'
+       Window.DrawProgressBar(0.8, 'Writing Packed Data to faces')
+       packedLs.sort(lambda A, B: cmp(A[0] , B[0])) # Sort by ID, so there in sync again
+       
+       islandIdx = len(islandList)
+       # Having these here avoids devide by 0
+       if islandIdx:
+               
+               if USER_STRETCH_ASPECT:
+                       # Maximize to uv area?? Will write a normalize function.
+                       xfactor = 1.0 / packWidth
+                       yfactor = 1.0 / packHeight      
+               else:
+                       # Keep proportions.
+                       xfactor = yfactor = 1.0 / max(packWidth, packHeight)
+       
+       while islandIdx:
+               islandIdx -=1
+               # Write the packed values to the UV's
+               
+               
+               xoffset = packedLs[islandIdx][1] - islandOffsetList[islandIdx][0]
+               yoffset = packedLs[islandIdx][2] - islandOffsetList[islandIdx][1]
+               
+               if USER_MARGIN:
+                       USER_MARGIN_SCALE = 1-(USER_MARGIN*2)
+                       for f in islandList[islandIdx]: # Offsetting the UV's so they fit in there packed box, margin
+                               f.uv = [Vector((((uv[0]+xoffset)*xfactor)*USER_MARGIN_SCALE)+USER_MARGIN, (((uv[1]+yoffset)*yfactor)*USER_MARGIN_SCALE)+USER_MARGIN) for uv in f.uv]
+               else:
+                       for f in islandList[islandIdx]: # Offsetting the UV's so they fit in there packed box
+                               f.uv = [Vector(((uv[0]+xoffset)*xfactor), ((uv[1]+yoffset)*yfactor)) for uv in f.uv]
+                       
+                       
+
+def VectoMat(vec):
+       
+       a3 = Vector(vec)
+       
+       a3.normalize()
+       
+       up = Vector([0,0,1])
+       if abs(DotVecs(a3, up)) == 1.0:
+               up = Vector([0,1,0])
+       
+       a1 = CrossVecs(a3, up)
+       a1.normalize()
+       a2 = CrossVecs(a3, a1)
+       return Matrix([a1[0], a1[1], a1[2]], [a2[0], a2[1], a2[2]], [a3[0], a3[1], a3[2]])
+
+
+global ob
+ob = None
+def main():
+       global USER_FILL_HOLES
+       global USER_FILL_HOLES_QUALITY
+       global USER_STRETCH_ASPECT
+       global USER_MARGIN
+       
+       # Use datanames as kesy so as not to unwrap a mesh more then once.
+       obList =  dict([(ob.getData(name_only=1), ob) for ob in Object.GetSelected() if ob.getType() == 'Mesh'])
+       
+       
+       # Face select object may not be selected.
+       scn = Scene.GetCurrent()
+       ob = scn.getActiveObject()
+       if ob and ob.sel == 0 and ob.getType() == 'Mesh':
+               # Add to the list
+               obList[ob.getData(name_only=1)] = ob
+       del scn # Sone use the scene again.
+       
+       obList = obList.values() # turn from a dict to a list.
+       
+       if not obList:
+               Draw.PupMenu('error, no selected mesh objects')
+               return
+       
+       # Create the variables.
+       USER_PROJECTION_LIMIT = Draw.Create(66)
+       USER_ONLY_SELECTED_FACES = Draw.Create(1)
+       USER_STRETCH_ASPECT = Draw.Create(1) # Only for hole filling.
+       USER_MARGIN = Draw.Create(0.0) # Only for hole filling.
+       USER_FILL_HOLES = Draw.Create(0)
+       USER_FILL_HOLES_QUALITY = Draw.Create(50) # Only for hole filling.
+       
+       
+       pup_block = [\
+       'Projection',\
+       ('Angle Limit:', USER_PROJECTION_LIMIT, 1, 89, 'lower for more projection groups, higher for less distortion.'),\
+       ('Selected Faces Only', USER_ONLY_SELECTED_FACES, 'Use only selected faces from all selected meshes.'),\
+       'UV Layout',\
+       ('Stretch to bounds', USER_STRETCH_ASPECT, 'Stretch the final output to texture bounds.'),\
+       ('Bleed Margin:', USER_MARGIN, 0.0, 0.25, 'Margin to reduce bleed from texture tiling.'),\
+       'Fill in empty areas',\
+       ('Fill Holes', USER_FILL_HOLES, 'Fill in empty areas reduced texture waistage (slow).'),\
+       ('Fill Quality:', USER_FILL_HOLES_QUALITY, 1, 100, 'Depends on fill holes, how tightly to fill UV holes, (higher is slower)'),\
+       ]
+       
+       # Reuse variable
+       if len(obList) == 1:
+               ob = "Unwrap %i Selected Mesh"
+       else:
+               ob = "Unwrap %i Selected Meshes"
+       
+       # HACK, loop until mouse is lifted.
+       '''
+       while Window.GetMouseButtons() != 0:
+               sys.sleep(10)
+       '''
+       
+       if not Draw.PupBlock(ob % len(obList), pup_block):
+               return
+       del ob
+       
+       # Convert from being button types
+       USER_PROJECTION_LIMIT = USER_PROJECTION_LIMIT.val
+       USER_ONLY_SELECTED_FACES = USER_ONLY_SELECTED_FACES.val
+       USER_STRETCH_ASPECT = USER_STRETCH_ASPECT.val
+       USER_MARGIN = USER_MARGIN.val
+       USER_FILL_HOLES = USER_FILL_HOLES.val
+       USER_FILL_HOLES_QUALITY = USER_FILL_HOLES_QUALITY.val
+       
+       
+       USER_PROJECTION_LIMIT_CONVERTED = cos(USER_PROJECTION_LIMIT * DEG_TO_RAD)
+       USER_PROJECTION_LIMIT_HALF_CONVERTED = cos((USER_PROJECTION_LIMIT/2) * DEG_TO_RAD)
+       
+       
+       # Toggle Edit mode
+       is_editmode = Window.EditMode()
+       if is_editmode:
+               Window.EditMode(0)
+       # Assume face select mode! an annoying hack to toggle face select mode because Mesh dosent like faceSelectMode.
+       
+               
+       Window.WaitCursor(1)
+       SELECT_FLAG = Mesh.FaceFlags['SELECT']
+       time1 = sys.time()
+       for ob in obList:
+               
+               # Only meshes
+               if ob.getType() != 'Mesh':
+                       continue
+               
+               me = ob.getData(mesh=1)
+               
+               if not me.faceUV: # Mesh has no UV Coords, dont bother.
+                       continue
+               
+               if USER_ONLY_SELECTED_FACES:
+                       meshFaces = [f for f in me.faces if f.flag & SELECT_FLAG]
+               else:
+                       meshFaces = [f for f in me.faces]
+               
+               if not meshFaces:
+                       continue
+               
+               #print '\n\n\nArchimap UV Unwrapper, mapping "%s", %i faces.' % (me.name, len(meshFaces))
+               Window.DrawProgressBar(0.1, 'Archimap UV Unwrapper, mapping "%s", %i faces.' % (me.name, len(meshFaces)))
+               
+               # Generate Projection
+               projectVecs = [] # We add to this allong the way
+               
+               # =======
+               # Generate a projection list from face normals, this is ment to be smart :)
+               
+               # make a list of face props that are in sync with meshFaces             
+               # Make a Face List that is sorted by area.
+               faceListProps = []              
+               
+               for f in meshFaces:
+                       area = faceArea(f)
+                       if area <= SMALL_NUM:
+                               f.uv = [Vector(0.0, 0.0)] * len(f.v) # Assign dummy UV
+                               print 'found zero area face, removing.'
+                               
+                       else:
+                               # Store all here
+                               n = f.no
+                               faceListProps.append( [f, area, Vector(n)] )
+               
+               del meshFaces
+               
+               faceListProps.sort( lambda A, B: cmp(B[1] , A[1]) ) # Biggest first.
+               # Smallest first is slightly more efficient, but if the user cancels early then its better we work on the larger data.
+               
+               # Generate Projection Vecs
+               # 0d is   1.0
+               # 180 IS -0.59846
+               
+               
+               # Initialize projectVecs
+               newProjectVec = faceListProps[0][2] 
+               newProjectFacePropList = [faceListProps[0]]     # Popping stuffs it up.
+               
+               # Predent that the most unique angke is ages away to start the loop off
+               mostUniqueAngle = -1.0
+               
+               # This is popped
+               tempFaceListProps = faceListProps[:]
+               
+               while 1:
+                       # If theres none there then start with the largest face
+                       
+                       # Pick the face thats most different to all existing angles :)
+                       mostUniqueAngle = 1.0 # 1.0 is 0d. no difference.
+                       mostUniqueIndex = 0 # fake
+                       
+                       fIdx = len(tempFaceListProps)
+                       while fIdx:
+                               fIdx-=1
+                               angleDifference = -1.0 # 180d difference.
+                               
+                               # Get the closest vec angle we are to.
+                               for p in projectVecs:
+                                       angleDifference = max(angleDifference, DotVecs(p, tempFaceListProps[fIdx][2]))
+                               
+                               if angleDifference < mostUniqueAngle:
+                                       # We have a new most different angle
+                                       mostUniqueIndex = fIdx
+                                       mostUniqueAngle = angleDifference
+                       
+                       
+                       if mostUniqueAngle < USER_PROJECTION_LIMIT_CONVERTED:
+                               #print 'adding', mostUniqueAngle, USER_PROJECTION_LIMIT, len(newProjectFacePropList)
+                               newProjectVec = tempFaceListProps[mostUniqueIndex][2]
+                               newProjectFacePropList = [tempFaceListProps.pop(mostUniqueIndex)]                               
+                       else:
+                               if len(projectVecs) >= 1: # Must have at least 2 projections
+                                       break
+                       
+                       
+                       # Now we have found the most different vector, add all the faces that are close.
+                       fIdx = len(tempFaceListProps)
+                       while fIdx:
+                               fIdx -= 1
+                               
+                               # Use half the angle limit so we dont overweight faces towards this
+                               # normal and hog all the faces.
+                               if DotVecs(newProjectVec, tempFaceListProps[fIdx][2]) > USER_PROJECTION_LIMIT_HALF_CONVERTED:
+                                       newProjectFacePropList.append(tempFaceListProps.pop(fIdx))
+                       
+                       
+                       # Now weight the vector to all its faces, will give a more direct projection
+                       # if the face its self was not representive of the normal from surrounding faces.
+                       averageVec = Vector([0,0,0])
+                       for fprop in newProjectFacePropList:
+                               averageVec += (fprop[2] * fprop[1]) # / len(newProjectFacePropList)
+                       
+                       if averageVec.x != 0 or averageVec.y != 0 or averageVec.z != 0: # Avoid NAN
+                               averageVec.normalize()                          
+                               projectVecs.append(averageVec)
+                       
+                       # Now we have used it, ignore it.
+                       newProjectFacePropList = []
+                       
+               # If there are only zero area faces then its possible
+               # there are no projectionVecs
+               if not len(projectVecs):
+                       Draw.PupMenu('error, no projection vecs where generated, 0 area faces can cause this.')
+                       return
+               
+               faceProjectionGroupList =[[] for i in xrange(len(projectVecs)) ]
+               faceProjectionGroupListArea =[[] for i in xrange(len(projectVecs)) ]
+               
+               # We need the area later, and we alredy have calculated it. so store it here.
+               #faceProjectionGroupListArea =[[] for i in xrange(len(projectVecs)) ]
+               
+               # MAP and Arrange # We know there are 3 or 4 faces here 
+               fIdx = len(faceListProps)
+               while fIdx:
+                       fIdx-=1
+                       fvec = Vector(faceListProps[fIdx][2])
+                       i = len(projectVecs)
+                       
+                       # Initialize first
+                       bestAng = DotVecs(fvec, projectVecs[0])
+                       bestAngIdx = 0
+                       
+                       # Cycle through the remaining, first alredy done
+                       while i-1:
+                               i-=1
+                               
+                               newAng = DotVecs(fvec, projectVecs[i])
+                               if newAng > bestAng: # Reverse logic for dotvecs
+                                       bestAng = newAng
+                                       bestAngIdx = i
+                       
+                       # Store the area for later use.
+                       faceProjectionGroupList[bestAngIdx].append(faceListProps[fIdx][0])
+                       faceProjectionGroupListArea[bestAngIdx].append(faceListProps[fIdx][1])
+                       
+               
+               # Cull faceProjectionGroupList,
+               
+               
+               # Now faceProjectionGroupList is full of faces that face match the project Vecs list
+               i= len(projectVecs)
+               while i:
+                       i-=1
+                       
+                       # Account for projectVecs having no faces.
+                       if not faceProjectionGroupList[i]:
+                               continue
+                                       
+                       # Make a projection matrix from a unit length vector.
+                       MatProj = VectoMat(projectVecs[i])
+                       
+                       # Get the faces UV's from the projected vertex.
+                       for f in faceProjectionGroupList[i]:
+                               f.uv = [MatProj * v.co for v in f.v]
+               
+               packLinkedUvs(faceProjectionGroupList, faceProjectionGroupListArea, me)
+               
+               print "ArchiMap time: %.2f" % (sys.time() - time1)
+               Window.DrawProgressBar(0.9, "ArchiMap Done, time: %.2f sec." % (sys.time() - time1))
+               
+               # Update and dont mess with edge data.
+               # OLD NMESH # me.update(0, (me.edges != []), 0)
+       
+       Window.DrawProgressBar(1.0, "")
+       Window.WaitCursor(0)
+       Window.RedrawAll()
+
+if __name__ == '__main__':
+       try:
+               main()
+               
+       except KeyboardInterrupt:
+               print '\nUser Canceled.'
+               Draw.PupMenu('user canceled execution, unwrap aborted.')
+               Window.DrawProgressBar(1.0, "")
+               Window.WaitCursor(0)
\ No newline at end of file
diff --git a/release/scripts/bpydata/mesh_bbrush.py b/release/scripts/bpydata/mesh_bbrush.py
new file mode 100644 (file)
index 0000000..eac576b
--- /dev/null
@@ -0,0 +1,919 @@
+# SPACEHANDLER.VIEW3D.EVENT
+# Dont run, event handelers are accessed in the from the 3d View menu.
+
+import Blender
+from Blender import Mathutils, Window, Scene, Draw, Mesh, NMesh
+from Blender.Mathutils import CrossVecs, Matrix, Vector, Intersect, LineIntersect
+
+
+# DESCRIPTION:
+# screen_x, screen_y the origin point of the pick ray
+# it is either the mouse location
+# localMatrix is used if you want to have the returned values in an objects localspace.
+#    this is usefull when dealing with an objects data such as verts.
+# or if useMid is true, the midpoint of the current 3dview
+# returns
+# Origin - the origin point of the pick ray
+# Direction - the direction vector of the pick ray
+# in global coordinates
+epsilon = 1e-3 # just a small value to account for floating point errors
+
+def getPickRay(screen_x, screen_y, localMatrix=None, useMid = False):
+       
+       # Constant function variables
+       p = getPickRay.p
+       d = getPickRay.d
+       
+       for win3d in Window.GetScreenInfo(Window.Types.VIEW3D): # we search all 3dwins for the one containing the point (screen_x, screen_y) (could be the mousecoords for example) 
+               win_min_x, win_min_y, win_max_x, win_max_y = win3d['vertices']
+               # calculate a few geometric extents for this window
+
+               win_mid_x  = (win_max_x + win_min_x + 1.0) * 0.5
+               win_mid_y  = (win_max_y + win_min_y + 1.0) * 0.5
+               win_size_x = (win_max_x - win_min_x + 1.0) * 0.5
+               win_size_y = (win_max_y - win_min_y + 1.0) * 0.5
+
+               #useMid is for projecting the coordinates when we subdivide the screen into bins
+               if useMid: # == True
+                       screen_x = win_mid_x
+                       screen_y = win_mid_y
+               
+               # if the given screencoords (screen_x, screen_y) are within the 3dwin we fount the right one...
+               if (win_max_x > screen_x > win_min_x) and (  win_max_y > screen_y > win_min_y):
+                       # first we handle all pending events for this window (otherwise the matrices might come out wrong)
+                       Window.QHandle(win3d['id'])
+                       
+                       # now we get a few matrices for our window...
+                       # sorry - i cannot explain here what they all do
+                       # - if you're not familiar with all those matrices take a look at an introduction to OpenGL...
+                       pm      = Window.GetPerspMatrix()   # the prespective matrix
+                       pmi  = Matrix(pm); pmi.invert() # the inverted perspective matrix
+                       
+                       if (1.0 - epsilon < pmi[3][3] < 1.0 + epsilon):
+                               # pmi[3][3] is 1.0 if the 3dwin is in ortho-projection mode (toggled with numpad 5)
+                               hms = getPickRay.hms
+                               ortho_d = getPickRay.ortho_d
+                               
+                               # ortho mode: is a bit strange - actually there's no definite location of the camera ...
+                               # but the camera could be displaced anywhere along the viewing direction.
+                               
+                               ortho_d.x, ortho_d.y, ortho_d.z = Window.GetViewVector()
+                               ortho_d.w = 0
+                               
+                               # all rays are parallel in ortho mode - so the direction vector is simply the viewing direction
+                               hms.x, hms.y, hms.z, hms.w = (screen_x-win_mid_x) /win_size_x, (screen_y-win_mid_y) / win_size_y, 0.0, 1.0
+                               
+                               # these are the homogenious screencoords of the point (screen_x, screen_y) ranging from -1 to +1
+                               p=(hms*pmi) + (1000*ortho_d)
+                               p.resize3D()
+                               d.x, d.y, d.z = ortho_d.x, ortho_d.y, ortho_d.z
+                               
+
+                       # Finally we shift the position infinitely far away in
+                       # the viewing direction to make sure the camera if outside the scene
+                       # (this is actually a hack because this function
+                       # is used in sculpt_mesh to initialize backface culling...)
+                       else:
+                               # PERSPECTIVE MODE: here everything is well defined - all rays converge at the camera's location
+                               vmi  = Matrix(Window.GetViewMatrix()); vmi.invert() # the inverse viewing matrix
+                               fp = getPickRay.fp
+                               
+                               dx = pm[3][3] * (((screen_x-win_min_x)/win_size_x)-1.0) - pm[3][0]
+                               dy = pm[3][3] * (((screen_y-win_min_y)/win_size_y)-1.0) - pm[3][1]
+                               
+                               fp.x, fp.y, fp.z = \
+                               pmi[0][0]*dx+pmi[1][0]*dy,\
+                               pmi[0][1]*dx+pmi[1][1]*dy,\
+                               pmi[0][2]*dx+pmi[1][2]*dy
+                               
+                               # fp is a global 3dpoint obtained from "unprojecting" the screenspace-point (screen_x, screen_y)
+                               #- figuring out how to calculate this took me quite some time.
+                               # The calculation of dxy and fp are simplified versions of my original code
+                               #- so it's almost impossible to explain what's going on geometrically... sorry
+                               
+                               p.x, p.y, p.z = vmi[3][:3]
+                               
+                               # the camera's location in global 3dcoords can be read directly from the inverted viewmatrix
+                               #d.x, d.y, d.z =normalize_v3(sub_v3v3(p, fp))
+                               d.x, d.y, d.z = p.x-fp.x, p.y-fp.y, p.z-fp.z
+                               
+                               #print 'd', d, 'p', p, 'fp', fp
+                               
+                       
+                       # the direction vector is simply the difference vector from the virtual camera's position
+                       #to the unprojected (screenspace) point fp
+                       
+                       # Do we want to return a direction in object's localspace?
+                       
+                       if localMatrix:
+                               localInvMatrix = Matrix(localMatrix)
+                               localInvMatrix.invert()
+                               p = p*localInvMatrix
+                               d = d*localInvMatrix # normalize_v3
+                               p.x += localInvMatrix[3][0]
+                               p.y += localInvMatrix[3][1]
+                               p.z += localInvMatrix[3][2]
+                               
+                       #else: # Worldspace, do nothing
+                       
+                       d.normalize()
+                       return True, p, d # Origin, Direction   
+       
+       # Mouse is not in any view, return None.
+       return False, None, None
+
+# Constant function variables
+getPickRay.d = Vector(0,0,0) # Perspective, 3d
+getPickRay.p = Vector(0,0,0)
+getPickRay.fp = Vector(0,0,0)
+
+getPickRay.hms = Vector(0,0,0,0) # ortho only 4d
+getPickRay.ortho_d = Vector(0,0,0,0) # ortho only 4d
+
+
+
+def ui_set_preferences(user_interface=1):
+       # Create data and set defaults.
+       ADAPTIVE_GEOMETRY_but = Draw.Create(0)
+       BRUSH_MODE_but = Draw.Create(1)
+       BRUSH_PRESSURE_but = Draw.Create(0.05)
+       BRUSH_RADIUS_but = Draw.Create(0.25)
+       RESOLUTION_MIN_but = Draw.Create(0.1)
+       DISPLACE_NORMAL_MODE_but = Draw.Create(2)
+       STATIC_NORMAL_but = Draw.Create(1)
+       XPLANE_CLIP_but = Draw.Create(0)
+       STATIC_MESH_but = Draw.Create(1)
+       FIX_TOPOLOGY_but = Draw.Create(0)
+       
+       # Remember old variables if alredy set.
+       try:
+               ADAPTIVE_GEOMETRY_but.val = Blender.bbrush['ADAPTIVE_GEOMETRY']
+               BRUSH_MODE_but.val = Blender.bbrush['BRUSH_MODE']
+               BRUSH_PRESSURE_but.val = Blender.bbrush['BRUSH_PRESSURE']
+               BRUSH_RADIUS_but.val = Blender.bbrush['BRUSH_RADIUS']
+               RESOLUTION_MIN_but.val = Blender.bbrush['RESOLUTION_MIN']
+               DISPLACE_NORMAL_MODE_but.val = Blender.bbrush['DISPLACE_NORMAL_MODE']
+               STATIC_NORMAL_but.val = Blender.bbrush['STATIC_NORMAL']
+               XPLANE_CLIP_but.val = Blender.bbrush['XPLANE_CLIP']
+               STATIC_MESH_but.val = Blender.bbrush['STATIC_MESH']
+               FIX_TOPOLOGY_but.val = Blender.bbrush['FIX_TOPOLOGY']
+       except:
+               Blender.bbrush = {}
+       
+       if user_interface:
+               pup_block = [\
+               'Brush Options',\
+               ('Adaptive Geometry', ADAPTIVE_GEOMETRY_but, 'Add and remove detail as needed. Uses min/max resolution.'),\
+               ('Brush Type: ', BRUSH_MODE_but, 1, 5, 'Push/Pull:1, Grow/Shrink:2, Spin:3, Relax:4, Goo:5'),\
+               ('Pressure: ', BRUSH_PRESSURE_but, 0.01, 1.0, 'Pressure of the brush.'),\
+               ('Size: ', BRUSH_RADIUS_but, 0.02, 2.0, 'Size of the brush.'),\
+               ('Geometry Res: ', RESOLUTION_MIN_but, 0.01, 0.5, 'Size of the brush & Adaptive Subdivision.'),\
+               ('Displace Vector: ', DISPLACE_NORMAL_MODE_but, 1, 4, 'Vertex Normal:1, Median Normal:2, Face Normal:3, View Normal:4'),\
+               ('Static Normal', STATIC_NORMAL_but, 'Use the initial normal only.'),\
+               ('No X Crossing', XPLANE_CLIP_but, 'Dont allow verts to have a negative X axis (use for x-mirror).'),\
+               ('Static Mesh', STATIC_MESH_but, 'During mouse interaction, dont update the mesh.'),\
+               #('Fix Topology', FIX_TOPOLOGY_but, 'Fix the mesh structure by rotating edges '),\
+               ]
+               
+               Draw.PupBlock('BlenBrush Prefs (RMB)', pup_block)
+       
+       Blender.bbrush['ADAPTIVE_GEOMETRY'] = ADAPTIVE_GEOMETRY_but.val
+       Blender.bbrush['BRUSH_MODE'] = BRUSH_MODE_but.val
+       Blender.bbrush['BRUSH_PRESSURE'] = BRUSH_PRESSURE_but.val
+       Blender.bbrush['BRUSH_RADIUS'] = BRUSH_RADIUS_but.val
+       Blender.bbrush['RESOLUTION_MIN'] = RESOLUTION_MIN_but.val
+       Blender.bbrush['DISPLACE_NORMAL_MODE'] = DISPLACE_NORMAL_MODE_but.val
+       Blender.bbrush['STATIC_NORMAL'] = STATIC_NORMAL_but.val
+       Blender.bbrush['XPLANE_CLIP'] = XPLANE_CLIP_but.val
+       Blender.bbrush['STATIC_MESH'] = STATIC_MESH_but.val
+       Blender.bbrush['FIX_TOPOLOGY'] = FIX_TOPOLOGY_but.val
+
+
+def triangulateNMesh(nm):
+       '''
+       Converts the meshes faces to tris, modifies the mesh in place.
+       '''
+       
+       #============================================================================#
+       # Returns a new face that has the same properties as the origional face      #
+       # but with no verts                                                       #
+       #============================================================================#
+       def copyFace(face):
+               newFace = NMesh.Face()
+               # Copy some generic properties
+               newFace.mode = face.mode
+               if face.image != None:
+                       newFace.image = face.image
+               newFace.flag = face.flag
+               newFace.mat = face.mat
+               newFace.smooth = face.smooth
+               return newFace
+       
+       # 2 List comprehensions are a lot faster then 1 for loop.
+       tris = [f for f in nm.faces if len(f) == 3]
+       quads = [f for f in nm.faces if len(f) == 4]
+       
+       
+       if quads: # Mesh may have no quads.
+               has_uv = quads[0].uv 
+               has_vcol = quads[0].col
+               for quadFace in quads:
+                       # Triangulate along the shortest edge
+                       #if (quadFace.v[0].co - quadFace.v[2].co).length < (quadFace.v[1].co - quadFace.v[3].co).length:
+                       a1 = Mathutils.TriangleArea(quadFace.v[0].co, quadFace.v[1].co, quadFace.v[2].co)
+                       a2 = Mathutils.TriangleArea(quadFace.v[0].co, quadFace.v[2].co, quadFace.v[3].co)
+                       b1 = Mathutils.TriangleArea(quadFace.v[1].co, quadFace.v[2].co, quadFace.v[3].co)
+                       b2 = Mathutils.TriangleArea(quadFace.v[1].co, quadFace.v[3].co, quadFace.v[0].co)
+                       a1,a2 = min(a1, a2), max(a1, a2)
+                       b1,b2 = min(b1, b2), max(b1, b2)
+                       if a1/a2 < b1/b2:
+                               
+                               # Method 1
+                               triA = 0,1,2
+                               triB = 0,2,3
+                       else:
+                               # Method 2
+                               triA = 0,1,3
+                               triB = 1,2,3
+                               
+                       for tri1, tri2, tri3 in (triA, triB):
+                               newFace = copyFace(quadFace)
+                               newFace.v = [quadFace.v[tri1], quadFace.v[tri2], quadFace.v[tri3]]
+                               if has_uv: newFace.uv = [quadFace.uv[tri1], quadFace.uv[tri2], quadFace.uv[tri3]]
+                               if has_vcol: newFace.col = [quadFace.col[tri1], quadFace.col[tri2], quadFace.col[tri3]]
+                               
+                               nm.addEdge(quadFace.v[tri1], quadFace.v[tri3]) # Add an edge where the 2 tris are devided.
+                               tris.append(newFace)
+               
+               nm.faces = tris
+
+import mesh_tri2quad
+def fix_topolagy(mesh):
+       ob = Scene.GetCurrent().getActiveObject()
+       
+       for f in mesh.faces:
+               f.sel = 1
+       mesh.quadToTriangle(0) 
+       nmesh = ob.getData()
+
+       mesh_tri2quad.tri2quad(nmesh, 100, 0)
+       triangulateNMesh(nmesh)
+       nmesh.update()
+       
+       mesh = Mesh.Get(mesh.name)
+       for f in mesh.faces:
+               f.sel=1 
+       mesh.quadToTriangle()
+       Mesh.Mode(Mesh.SelectModes['EDGE'])
+       
+       
+       
+       
+
+
+def event_main():
+       #print Blender.event
+       #mod =[Window.Qual.CTRL,  Window.Qual.ALT, Window.Qual.SHIFT]
+       mod =[Window.Qual.CTRL,  Window.Qual.ALT]
+       
+       qual = Window.GetKeyQualifiers()
+       SHIFT_FLAG = Window.Qual.SHIFT
+       CTRL_FLAG = Window.Qual.CTRL
+       
+       
+       # UNDO
+       """
+       is_editmode = Window.EditMode() # Exit Editmode.
+       if is_editmode: Window.EditMode(0)
+       if Blender.event == Draw.UKEY:
+               if is_editmode:
+                       Blender.event = Draw.UKEY
+                       return
+               else:
+                       winId = [win3d for win3d in Window.GetScreenInfo(Window.Types.VIEW3D)][0]
+                       Blender.event = None
+                       Window.QHandle(winId['id'])
+                       Window.EditMode(1)
+                       Window.QHandle(winId['id'])
+                       Window.QAdd(winId['id'],Draw.UKEY,1) # Change KeyPress Here for EditMode
+                       Window.QAdd(winId['id'],Draw.UKEY,0)
+                       Window.QHandle(winId['id'])
+                       Window.EditMode(0)
+                       Blender.event = None
+                       return
+       """
+       
+       ob = Scene.GetCurrent().getActiveObject()
+       if not ob or ob.getType() != 'Mesh':
+               return
+       
+       # Mouse button down with no modifiers.  
+       if Blender.event == Draw.LEFTMOUSE and not [True for m in mod if m & qual]:
+               # Do not exit (draw)
+               pass
+       elif Blender.event == Draw.RIGHTMOUSE and not [True for m in mod if m & qual]:
+               ui_set_preferences()
+               return
+       else:
+               return 
+               
+       del qual
+       
+       
+       try:
+               Blender.bbrush
+       except:
+               # First time run
+               ui_set_preferences() # No ui
+               return
+
+       ADAPTIVE_GEOMETRY = Blender.bbrush['ADAPTIVE_GEOMETRY'] # 1
+       BRUSH_MODE = Blender.bbrush['BRUSH_MODE'] # 1
+       BRUSH_PRESSURE_ORIG = Blender.bbrush['BRUSH_PRESSURE'] # 0.1
+       BRUSH_RADIUS = Blender.bbrush['BRUSH_RADIUS'] # 0.5
+       RESOLUTION_MIN = Blender.bbrush['RESOLUTION_MIN'] # 0.08
+       STATIC_NORMAL = Blender.bbrush['STATIC_NORMAL'] # 0
+       XPLANE_CLIP = Blender.bbrush['XPLANE_CLIP'] # 0
+       DISPLACE_NORMAL_MODE = Blender.bbrush['DISPLACE_NORMAL_MODE'] # 'Vertex Normal%x1|Median Normal%x2|Face Normal%x3|View Normal%x4'
+       STATIC_MESH = Blender.bbrush['STATIC_MESH']
+       FIX_TOPOLOGY = Blender.bbrush['FIX_TOPOLOGY']
+       
+       
+       # Angle between Vecs wrapper.
+       AngleBetweenVecs = Mathutils.AngleBetweenVecs
+       def ang(v1,v2):
+               try:
+                       return AngleBetweenVecs(v1,v2)
+               except:
+                       return 180
+       """
+       def Angle2D(x1, y1, x2, y2):
+               import math
+               RAD2DEG = 57.295779513082323
+               '''
+                  Return the angle between two vectors on a plane
+                  The angle is from vector 1 to vector 2, positive anticlockwise
+                  The result is between -pi -> pi
+               '''
+               dtheta = math.atan2(y2,x2) - math.atan2(y1,x1) # theta1 - theta2
+               while dtheta > math.pi:
+                       dtheta -= (math.pi*2)
+               while dtheta < -math.pi:
+                       dtheta += (math.pi*2)
+               return dtheta * RAD2DEG  #(180.0 / math.pi)
+       """
+       
+       def faceIntersect(f):
+               isect = Intersect(f.v[0].co, f.v[1].co, f.v[2].co, Direction, Origin, 1) # Clipped.
+               if isect:
+                       return isect
+               elif len(f.v) == 4:
+                       isect = Intersect(f.v[0].co, f.v[2].co, f.v[3].co, Direction, Origin, 1) # Clipped.
+               return isect
+       """
+       # Unused so farm, too slow.
+       def removeDouble(v1,v2, me):
+               v1List = [f for f in me.faces if v1 in f.v]
+               v2List = [f for f in me.faces if v2 in f.v]
+               #print v1List
+               #print v2List
+               remFaces = []
+               newFaces = []
+               for f2 in v2List:
+                       f2ls = list(f2.v)
+                       i = f2ls.index(v2)
+                       f2ls[i] = v1
+                       #remFaces.append(f2)
+                       if f2ls.count(v1) == 1:
+                               newFaces.append(tuple(f2ls))
+               if remFaces:
+                       me.faces.delete(1, remFaces)
+               #me.verts.delete(v2)
+               if newFaces:
+                       me.faces.extend(newFaces)
+       """
+       
+       
+       me = ob.getData(mesh=1)
+       
+       is_editmode = Window.EditMode() # Exit Editmode.
+       if is_editmode: Window.EditMode(0)
+       
+       Mesh.Mode(Mesh.SelectModes['EDGE'])
+       
+       # At the moment ADAPTIVE_GEOMETRY is the only thing that uses selection.
+       if ADAPTIVE_GEOMETRY:
+               # Deslect all
+               SEL_FLAG = Mesh.EdgeFlags['SELECT']
+               '''
+               for ed in me.edges:
+                       #ed.flag &= ~SEL_FLAG # deselect. 34
+                       ed.flag = 32
+               '''
+               #filter(lambda ed: setattr(ed, 'flag', 32), me.edges)
+               
+               '''for v in me.verts:
+                       v.sel = 0'''
+               #filter(lambda v: setattr(v, 'sel', 0), me.verts)
+               # DESELECT ABSOLUTLY ALL
+               Mesh.Mode(Mesh.SelectModes['FACE'])
+               filter(lambda f: setattr(f, 'sel', 0), me.faces)
+               
+               Mesh.Mode(Mesh.SelectModes['EDGE'])
+               filter(lambda ed: setattr(ed, 'flag', 32), me.edges)
+               
+               Mesh.Mode(Mesh.SelectModes['VERTEX'])
+               filter(lambda v: setattr(v, 'sel', 0), me.verts)                
+               
+               Mesh.Mode(Mesh.SelectModes['EDGE'])
+               
+       i = 0
+       time = Blender.sys.time()
+       last_best_isect = None # used for goo only
+       old_screen_x, old_screen_y = 1<<30, 1<<30
+       goo_dir_vec = last_goo_dir_vec = gooRotMatrix = None # goo mode only.
+       
+       # Normal stuff
+       iFaceNormal = medainNormal = None
+       
+       # Store all vert normals for now.
+       if BRUSH_MODE == 1 and STATIC_NORMAL: # Push pull
+               vert_orig_normals = dict([(v, v.no) for v in me.verts])
+       
+       elif BRUSH_MODE == 4: # RELAX, BUILD EDGE CONNECTIVITE DATA.
+               # we need edge connectivity
+               #vertEdgeUsers = [list() for i in xrange(len(me.verts))]
+               verts_connected_by_edge = [list() for i in xrange(len(me.verts))]
+               
+               for ed in me.edges:
+                       i1, i2 = ed.v1.index,  ed.v2.index
+                       #vertEdgeUsers[i1].append(ed)
+                       #vertEdgeUsers[i2].append(ed)
+                       
+                       verts_connected_by_edge[i1].append(ed.v2)
+                       verts_connected_by_edge[i2].append(ed.v1)
+       
+       if STATIC_MESH:
+               
+               # Try and find a static mesh to reuse.
+               # this is because we dont want to make a new mesh for each stroke.
+               mesh_static = None
+               for _me_name_ in Blender.NMesh.GetNames():
+                       _me_ = Mesh.Get(_me_name_)
+                       #print _me_.users , len(me.verts)
+                       if _me_.users == 0 and len(_me_.verts) == 0:
+                               mesh_static = _me_
+                               #print 'using', _me_.name
+                               break
+               del _me_name_
+               del _me_
+               
+               if not mesh_static:
+                       mesh_static = Mesh.New()
+                       print 'Making new mesh', mesh_static.name
+               
+               mesh_static.verts.extend([v.co for v in me.verts])
+               mesh_static.faces.extend([tuple([mesh_static.verts[v.index] for v in f.v]) for f in me.faces])
+       
+       
+       best_isect = gooPlane = None 
+       
+       while Window.GetMouseButtons() == 1:
+               i+=1
+               screen_x, screen_y = Window.GetMouseCoords()
+               
+               # Skip when no mouse movement, Only for Goo!
+               if screen_x == old_screen_x and screen_y == old_screen_y:
+                       if BRUSH_MODE == 5: # Dont modify while mouse is not moved for goo.
+                               continue
+               else: # mouse has moved get the new mouse ray.
+                       old_screen_x, old_screen_y = screen_x, screen_y
+                       mouseInView, Origin, Direction = getPickRay(screen_x, screen_y, ob.matrixWorld)
+                       if not mouseInView or not Origin:
+                               return
+                       Origin_SCALE = Origin * 100 
+                       
+               # Find an intersecting face!
+               bestLen = 1<<30 # start with an assumed realy bad match.
+               best_isect = None # last intersect is used for goo.
+               best_face = None
+               
+               if not last_best_isect:
+                       last_best_isect = best_isect
+               
+               if not mouseInView:
+                       last_best_isect = None  
+                       
+               else:
+                       # Find Face intersection closest to the view. 
+                       #for f in [f for f in me.faces if ang(f.no, Direction) < 90]:
+                       
+                       # Goo brush only intersects faces once, after that the brush follows teh view plain.
+                       if BRUSH_MODE == 5 and gooPlane != None and gooPlane:
+                               best_isect = Intersect( gooPlane[0], gooPlane[1], gooPlane[2], Direction, Origin, 0) # Non clipped
+                       else:
+                               if STATIC_MESH:
+                                       intersectingFaces = [(f, ix) for f in mesh_static.faces for ix in (faceIntersect(f),) if ix]
+                               else:
+                                       intersectingFaces = [(f, ix) for f in me.faces for ix in (faceIntersect(f),) if ix]
+                               
+                               for f, isect in intersectingFaces:
+                                       l = (Origin_SCALE-isect).length 
+                                       if l < bestLen:
+                                               best_face = f
+                                               best_isect = isect
+                                               bestLen = l
+               
+               if not best_isect:
+                       # Dont interpolate once the mouse moves off the mesh.
+                       lastGooVec = last_best_isect = None
+                       
+               else: # mouseInView must be true also
+                       
+                       # Use the shift key to modify the pressure.
+                       if SHIFT_FLAG & Window.GetKeyQualifiers():
+                               BRUSH_PRESSURE = -BRUSH_PRESSURE_ORIG
+                       else:
+                               BRUSH_PRESSURE =  BRUSH_PRESSURE_ORIG
+                       
+                       brush_verts = [(v,le) for v in me.verts for le in ((v.co-best_isect).length,) if le < BRUSH_RADIUS]
+                       
+                       # SETUP ONCE ONLY VARIABLES
+                       if STATIC_NORMAL: # Only set the normal once.
+                               if not iFaceNormal:
+                                       iFaceNormal = best_face.no
+                       else:
+                               if best_face:
+                                       iFaceNormal = best_face.no
+                       
+                       
+                       if DISPLACE_NORMAL_MODE == 2: # MEDIAN NORMAL
+                               if (STATIC_NORMAL and medainNormal == None) or not STATIC_NORMAL:
+                                       medainNormal = Vector(0,0,0)
+                                       for v, l in brush_verts:
+                                               medainNormal += v.no*(BRUSH_RADIUS-l)
+                                       medainNormal.normalize()
+                       
+                       
+                       # ================================================================#
+                       # == Tool code, loop on the verts and operate on them ============#
+                       # ================================================================#
+                       if BRUSH_MODE == 1: # NORMAL PAINT
+                               for v,l in brush_verts:
+                                       if XPLANE_CLIP:
+                                               origx = False
+                                               if abs(v.co.x) < 0.001: origx = True
+                                                       
+                                       
+                                       v.sel = 1 # MARK THE VERT AS DIRTY.
+                                       falloff = (BRUSH_RADIUS-l) / BRUSH_RADIUS # falloff between 0 and 1
+                                       
+                                       if DISPLACE_NORMAL_MODE == 1: # VERTEX NORMAL
+                                               if STATIC_NORMAL:
+                                                       try:
+                                                               no = vert_orig_normals[v]
+                                                       except:
+                                                               no = vert_orig_normals[v] = v.no
+                                                       v.co += (no * BRUSH_PRESSURE) * falloff
+                                               else:
+                                                       v.co += (v.no * BRUSH_PRESSURE) * falloff
+                                       elif DISPLACE_NORMAL_MODE == 2: # MEDIAN NORMAL # FIXME
+                                               v.co += (medainNormal * BRUSH_PRESSURE) * falloff
+                                       elif DISPLACE_NORMAL_MODE == 3: # FACE NORMAL
+                                               v.co += (iFaceNormal * BRUSH_PRESSURE) * falloff
+                                       elif DISPLACE_NORMAL_MODE == 4: # VIEW NORMAL
+                                               v.co += (Direction * BRUSH_PRESSURE) * falloff
+                                       
+                                       # Clamp back to original x if needs be.
+                                       if XPLANE_CLIP and origx:
+                                               v.co.x = 0
+                       
+                       elif BRUSH_MODE == 2: # SCALE
+                               for v,l in brush_verts:
+                                       
+                                       if XPLANE_CLIP:
+                                               origx = False
+                                               if abs(v.co.x) < 0.001: origx = True
+                                       
+                                       v.sel = 1 # MARK THE VERT AS DIRTY.
+                                       falloff = (BRUSH_RADIUS-l) / BRUSH_RADIUS # falloff between 0 and 1
+                                       
+                                       vert_scale_vec = v.co - best_isect
+                                       vert_scale_vec.normalize()
+                                       # falloff needs to be scaled for this tool
+                                       falloff = falloff / 10
+                                       v.co += (vert_scale_vec * BRUSH_PRESSURE) * falloff # FLAT BRUSH
+                                       
+                                       # Clamp back to original x if needs be.
+                                       if XPLANE_CLIP and origx:
+                                               v.co.x = 0
+                       
+                       if BRUSH_MODE == 3: # ROTATE.
+                               
+                               if DISPLACE_NORMAL_MODE == 1: # VERTEX NORMAL
+                                       ROTATE_MATRIX = Mathutils.RotationMatrix(BRUSH_PRESSURE*10, 4, 'r', iFaceNormal)  # Cant use vertex normal, use face normal
+                               elif DISPLACE_NORMAL_MODE == 2: # MEDIAN NORMAL
+                                       ROTATE_MATRIX = Mathutils.RotationMatrix(BRUSH_PRESSURE*10, 4, 'r', medainNormal)  # Cant use vertex normal, use face normal
+                               elif DISPLACE_NORMAL_MODE == 3: # FACE NORMAL
+                                       ROTATE_MATRIX = Mathutils.RotationMatrix(BRUSH_PRESSURE*10, 4, 'r', iFaceNormal)  # Cant use vertex normal, use face normal
+                               elif DISPLACE_NORMAL_MODE == 4: # VIEW NORMAL
+                                       ROTATE_MATRIX = Mathutils.RotationMatrix(BRUSH_PRESSURE*10, 4, 'r', Direction)  # Cant use vertex normal, use face normal
+                               # Brush code
+                               
+                               for v,l in brush_verts:
+                                       
+                                       if XPLANE_CLIP:
+                                               origx = False
+                                               if abs(v.co.x) < 0.001: origx = True
+                                       
+                                       # MARK THE VERT AS DIRTY.
+                                       v.sel = 1
+                                       falloff = (BRUSH_RADIUS-l) / BRUSH_RADIUS # falloff between 0 and 1
+                               
+                                       # Vectors handeled with rotation matrix creation.
+                                       rot_vert_loc = (ROTATE_MATRIX * (v.co-best_isect)) + best_isect
+                                       v.co = (v.co*(1-falloff)) + (rot_vert_loc*(falloff))
+                                       
+                                       # Clamp back to original x if needs be.
+                                       if XPLANE_CLIP and origx:
+                                               v.co.x = 0
+                               
+                       elif BRUSH_MODE == 4: # RELAX
+                               vert_orig_loc = [Vector(v.co) for v in me.verts ] # save orig vert location.
+                               #vertOrigNor = [Vector(v.no) for v in me.verts ] # save orig vert location.
+                               
+                               # Brush code
+                               for v,l in brush_verts:
+                                       
+                                       if XPLANE_CLIP:
+                                               origx = False
+                                               if abs(v.co.x) < 0.001: origx = True
+                                       
+                                       v.sel = 1 # Mark the vert as dirty.
+                                       falloff = (BRUSH_RADIUS-l) / BRUSH_RADIUS # falloff between 0 and 1
+                                       connected_verts = verts_connected_by_edge[v.index]
+                                       relax_point = reduce(lambda a,b: a + vert_orig_loc[b.index], connected_verts, Mathutils.Vector(0,0,0)) * (1.0/len(connected_verts))
+                                       falloff = falloff * BRUSH_PRESSURE
+                                       # Old relax.
+                                       #v.co = (v.co*(1-falloff)) + (relax_point*(falloff))
+                                       
+                                       ll = (v.co-relax_point).length
+                                       newpoint = (v.co*(1-falloff)) + (relax_point*(falloff)) - v.co
+                                       newpoint = newpoint * (1/(1+ll))
+                                       v.co = v.co + newpoint
+                                       
+                                       '''
+                                       # New relax
+                                       relax_normal = vertOrigNor[v.index]
+                                       v1,v2,v3,v4 = v.co, v.co+relax_normal, relax_point-(relax_normal*10), relax_point+(relax_normal*10)
+                                       print v1,v2,v3,v4
+                                       try:
+                                               a,b = LineIntersect(v1,v2,v3,v4) # Scale the normal to make a line. we know we will intersect with.
+                                               v.co = (v.co*(1-falloff)) + (a*(falloff))
+                                       except:
+                                               pass
+                                       '''
+                                       
+                                       # Clamp back to original x if needs be.
+                                       if XPLANE_CLIP and origx:
+                                               v.co.x = 0
+                                       
+                       elif BRUSH_MODE == 5: # GOO
+                               #print last_best_isect, best_isect, 'AA'
+                               if not last_best_isect:
+                                       last_best_isect = best_isect
+                                       
+                                       # Set up a triangle orthographic to the view plane
+                                       gooPlane = [best_isect, CrossVecs(best_isect, Direction), None]
+                                       
+                                       
+                                       if DISPLACE_NORMAL_MODE == 4: # View Normal
+                                               tempRotMatrix = Mathutils.RotationMatrix(90, 3, 'r', Direction)
+                                       else:
+                                               tempRotMatrix = Mathutils.RotationMatrix(90, 3, 'r', CrossVecs(best_face.no, Direction))
+                                       
+                                       gooPlane[2] =  best_isect + (tempRotMatrix * gooPlane[1])
+                                       gooPlane[1] = gooPlane[1] + best_isect
+                                       
+                                       continue # we need another point of reference.
+                                       
+                               elif last_best_isect == best_isect:
+                                       # Mouse has not moved, no point in trying to goo.
+                                       continue
+                               else:
+                                       if goo_dir_vec:
+                                               last_goo_dir_vec = goo_dir_vec
+                                       # The direction the mouse moved in 3d space. use for gooing
+                                       
+                                       # Modify best_isect so its not moving allong the view z axis.
+                                       # Assume Origin hasnt changed since the view wont change while the mouse is drawing. ATM.
+                                       best_isect = Intersect( gooPlane[0], gooPlane[1], gooPlane[2], Direction, Origin, 0) # Non clipped
+                                       goo_dir_vec = (best_isect - last_best_isect) * 2
+                                       
+                                       
+                                       # make a goo rotation matrix so the head of the goo rotates with the mouse.
+                                       """
+                                       if last_goo_dir_vec and goo_dir_vec != last_goo_dir_vec:
+                                               '''
+                                               vmi  = Matrix(Window.GetViewMatrix()); vmi.invert() # the inverse viewing matrix
+                                               a = last_goo_dir_vec * vmi
+                                               b = goo_dir_vec * vmi
+                                               c = Angle2D(a.x, a.y, b.x, b.y)
+                                               gooRotMatrix = Mathutils.RotationMatrix((c * goo_dir_vec.length)*-20, 3, 'r', Direction)
+                                               '''
+                                               pass
+                                       else:
+                                               gooRotMatrix = None
+                                       """
+                                       
+                                       if goo_dir_vec.x == 0 and goo_dir_vec.y == 0 and goo_dir_vec.z == 0:
+                                               continue
+                                       
+                                       # Brush code
+                                       for v,l in brush_verts:
+                                               
+                                               if XPLANE_CLIP:
+                                                       origx = False
+                                                       if abs(v.co.x) < 0.001: origx = True
+                                               
+                                               # MARK THE VERT AS DIRTY.
+                                               v.sel = 1
+                                               
+                                               ''' # ICICLES!!!
+                                               a = AngleBetweenVecs(goo_dir_vec, v.no)
+                                               if a > 66:
+                                                       continue
+                                                       
+                                               l = l * ((1+a)/67.0)
+                                               l = max(0.00000001, l)
+                                               '''
+                                               
+                                               falloff = (BRUSH_RADIUS-l) / BRUSH_RADIUS # falloff between 0 and 1
+                                               goo_loc = (v.co*(1-falloff)) + ((v.co+goo_dir_vec) *falloff)
+                                               
+                                               v.co = (goo_loc*BRUSH_PRESSURE) + (v.co*(1-BRUSH_PRESSURE))
+                                               
+                                               '''
+                                               if gooRotMatrix:
+                                                       rotatedVertLocation = (gooRotMatrix * (v.co-best_isect)) + best_isect
+                                                       v.co = (v.co*(1-falloff)) + (rotatedVertLocation*(falloff))
+                                                       # USe for goo only.                                     
+                                               '''
+                                               
+                                               # Clamp back to original x if needs be.
+                                               if XPLANE_CLIP and origx:
+                                                       v.co.x = 0
+                       
+                               
+                       # Remember for the next sample
+                       last_best_isect = best_isect
+                       last_goo_dir_vec = goo_dir_vec
+                       
+                       # Post processing after the verts have moved
+                       # Subdivide any large edges, all but relax.
+                       
+                       MAX_SUBDIV = 10 # Maximum number of subdivisions per redraw. makes things useable.
+                       SUBDIV_COUNT = 0
+                       # Cant use adaptive geometry for relax because it keeps connectivity data.
+                       if ADAPTIVE_GEOMETRY and (BRUSH_MODE == 1 or BRUSH_MODE == 2 or BRUSH_MODE == 3 or BRUSH_MODE == 5):
+                               Mesh.Mode(Mesh.SelectModes['EDGE'])
+                               orig_len_edges = 0
+                               #print 'ADAPTIVE_GEOMETRY'
+                               while len(me.edges) != orig_len_edges and SUBDIV_COUNT < MAX_SUBDIV:
+                                       #print 'orig_len_edges', len(me.edges) 
+                                       #me = ob.getData(mesh=1)
+                                       orig_len_edges = len(me.edges)
+                                       EDGE_COUNT = 0
+                                       for ed in me.edges:
+                                               if ed.v1.sel or ed.v2.sel:
+                                                       l = (ed.v1.co - ed.v2.co).length
+                                                       if l > max(RESOLUTION_MIN*1.5, BRUSH_RADIUS):
+                                                       #if l > BRUSH_RADIUS:
+                                                               #print 'adding edge'
+                                                               ed.flag |= SEL_FLAG
+                                                               #ed.flag = 35
+                                                               SUBDIV_COUNT += 1
+                                                               EDGE_COUNT +=1
+                                                       """
+                                                       elif l < RESOLUTION_MIN:
+                                                               '''
+                                                               print 'removing edge'
+                                                               v1 =e.v1
+                                                               v2 =e.v2
+                                                               v1.co = v2.co = (v1.co + v2.co) * 0.5
+                                                               v1.sel = v2.sel = 1
+                                                               me.remDoubles(0.001)
+                                                               me = ob.getData(mesh=1)
+                                                               break
+                                                               '''
+                                                               # Remove edge in python
+                                                               print 'removing edge'
+                                                               v1 =ed.v1
+                                                               v2 =ed.v2
+                                                               v1.co = v2.co = (v1.co + v2.co) * 0.5
+                                                               
+                                                               removeDouble(v1, v2, me)
+                                                               me = ob.getData(mesh=1)
+                                                               break
+                                                       """             
+                                                       
+                                       if EDGE_COUNT:
+                                               me.subdivide(1)
+                                               me = ob.getData(mesh=1)
+                                               filter(lambda ed: setattr(ed, 'flag', 32), me.edges)
+                                                       
+                                       # Deselect all, we know theres only 2 selected
+                                       '''
+                                       for ee in me.edges:
+                                               if ee.flag & SEL_FLAG:
+                                                       #ee.flag &= ~SEL_FLAG
+                                                       ee.flag = 32
+                                               elif l < RESOLUTION_MIN:
+                                                       print 'removing edge'
+                                                       e.v1.co = e.v2.co = (e.v1.co + e.v2.co) * 0.5
+                                                       me.remDoubles(0.001)
+                                                       break
+                                               '''
+                               # Done subdividing
+                               # Now remove doubles
+                               #print Mesh.SelectModes['VERT']
+                               #Mesh.Mode(Mesh.SelectModes['VERTEX'])
+                               
+                               filter(lambda v: setattr(v, 'sel', 1), me.verts)
+                               filter(lambda v: setattr(v[0], 'sel', 0), brush_verts)
+                               
+                               # Cycling editmode is too slow.
+                               
+                               remdoubles = False
+                               for ed in me.edges:
+                                       
+                                       if (not ed.v1.sel) and (not ed.v1.sel):
+                                               if XPLANE_CLIP:
+                                                       # If 1 vert is on the edge and abother is off dont collapse edge.
+                                                       if (abs(ed.v1.co.x) < 0.001) !=\
+                                                       (abs(ed.v2.co.x) < 0.001):
+                                                               continue
+                                               l = (ed.v1.co - ed.v2.co).length
+                                               if l < RESOLUTION_MIN:
+                                                       ed.v1.sel = ed.v2.sel = 1
+                                                       newco = (ed.v1.co + ed.v2.co)*0.5
+                                                       ed.v1.co.x = ed.v2.co.x = newco.x
+                                                       ed.v1.co.y = ed.v2.co.y = newco.y
+                                                       ed.v1.co.z = ed.v2.co.z = newco.z
+                                                       remdoubles = True
+                               
+                               #if remdoubles:
+
+                               
+                               filter(lambda v: setattr(v, 'sel', 0), me.verts)
+                               #Mesh.Mode(Mesh.SelectModes['EDGE'])
+                               # WHILE OVER
+                               # Clean up selection.
+                               #for v in me.verts:
+                               #       v.sel = 0
+                               '''
+                               for ee in me.edges:
+                                       if ee.flag & SEL_FLAG:
+                                               ee.flag &= ~SEL_FLAG
+                                               #ee.flag = 32
+                               '''
+                               filter(lambda ed: setattr(ed, 'flag', 32), me.edges)
+                               
+                               
+                       if XPLANE_CLIP:
+                               filter(lambda v: setattr(v.co, 'x', max(0, v.co.x)), me.verts)
+                       
+                       
+                       me.update()
+                       #Window.SetCursorPos(best_isect.x, best_isect.y, best_isect.z)
+                       Window.Redraw(Window.Types.VIEW3D)
+       #Window.DrawProgressBar(1.0, '')
+       if STATIC_MESH:
+               #try:
+               mesh_static.verts =  None
+               print len(mesh_static.verts)
+               mesh_static.update()
+               #except:
+               #       pass
+       if FIX_TOPOLOGY:
+               fix_topolagy(me)
+       
+       # Remove short edges of we have edaptive geometry enabled.
+       if ADAPTIVE_GEOMETRY:
+               Mesh.Mode(Mesh.SelectModes['VERTEX'])
+               filter(lambda v: setattr(v, 'sel', 1), me.verts)
+               me.remDoubles(0.001)
+               print 'removing doubles'
+               me = ob.getData(mesh=1) # Get new vert data
+               Blender.event = Draw.LEFTMOUSE
+
+       Mesh.Mode(Mesh.SelectModes['EDGE'])
+       
+       if i:
+               Window.EditMode(1)
+               if not is_editmode: # User was in edit mode, so stay there.
+                       Window.EditMode(0)
+               print '100 draws in %.6f' % (((Blender.sys.time()-time) / float(i))*100)
+
+if __name__ == '__main__':
+       event_main()
\ No newline at end of file
diff --git a/release/scripts/bpymodules/boxpack2d.py b/release/scripts/bpymodules/boxpack2d.py
new file mode 100644 (file)
index 0000000..5acfe06
--- /dev/null
@@ -0,0 +1,478 @@
+'''
+# 2D Box packing function used by archimap
+# packs any list of 2d boxes into a square and returns a list of packed boxes.
+# Example of usage.
+import boxpack2d
+
+# Build boxe list.
+# the unique ID is not used.
+# just the width and height.
+boxes2Pack = []
+anyUniqueID = 0; w = 2.2; h = 3.8
+boxes2Pack.append([anyUniqueID, w,h])
+anyUniqueID = 1; w = 4.1; h = 1.2
+boxes2Pack.append([anyUniqueID, w,h])
+anyUniqueID = 2; w = 5.2; h = 9.2
+boxes2Pack.append([anyUniqueID, w,h])
+anyUniqueID = 3; w = 8.3; h = 7.3
+boxes2Pack.append([anyUniqueID, w,h])
+anyUniqueID = 4; w = 1.1; h = 5.1
+boxes2Pack.append([anyUniqueID, w,h])
+anyUniqueID = 5; w = 2.9; h = 8.1
+boxes2Pack.append([anyUniqueID, w,h])
+anyUniqueID = 6; w = 4.2; h = 6.2
+boxes2Pack.append([anyUniqueID, w,h])
+# packedLs is a list of [(anyUniqueID, left, bottom, width, height)...]
+packWidth, packHeight, packedLs = boxpack2d.boxPackIter(boxes2Pack)
+'''
+
+from Blender import NMesh, Window
+
+# a box packing vert
+class vt:
+       def __init__(self, x,y):
+               self.x, self.y = x, y
+               
+               self.free = 15
+               
+               # Set flags so cant test bottom left of 0/0
+               #~ BLF = 1; TRF = 2; TLF = 4; BRF = 8
+               
+               #self.users = [] # A list of boxes.
+               # Rather then users, store Quadrents
+               self.blb = self.tlb = self.brb = self.trb = None
+               
+               
+               # A hack to remember the box() that last intersectec this vert
+               self.intersectCache = ([], [], [], [])
+               
+class vertList:        
+       def __init__(self, verts=[]):
+               self.verts = verts
+       
+       # Sorts closest first. - uses the box w/h as a bias,
+       # this makes it so its less likely to have lots of poking out bits
+       # that use too much 
+       # Lambada based sort
+       def sortCorner(self,w,h):
+               self.verts.sort(lambda A, B: cmp(max(A.x+w, A.y+h) , max(B.x+w, B.y+h))) # Reverse area sort
+               
+
+class box:
+       def __init__(self, width, height, id=None):
+               
+               self.id= id
+               
+               self.area = width * height # real area
+               self.farea = width + height # fake area
+               #self.farea = float(min(width, height)) / float(max(width, height))  # fake area
+               
+               self.width = width
+               self.height = height
+               
+               # Append 4 new verts
+               # (BL,TR,TL,BR) / 0,1,2,3
+               self.v = [vt(0,0), vt(width,height), vt(0,height), vt(width,0)]
+               
+               # Set the interior quadrents as used.
+               self.v[0].free &= ~TRF
+               self.v[1].free &= ~BLF
+               self.v[2].free &= ~BRF
+               self.v[3].free &= ~TLF
+               
+               #for v in self.v:
+               #       v.users.append(self)
+               self.v[0].trb = self
+               self.v[1].blb = self
+               self.v[2].brb = self
+               self.v[3].tlb = self
+               
+               
+       
+       # Updates verts 3 & 4 from 1 and 2
+       # since 3 and 4 are only there foill need is resizing/ rotating of patterns on the fly while I painr new box placement
+       # but may be merged later with other verts
+       def updateV34(self):
+               
+               self.v[TL].x = self.v[BL].x
+               self.v[TL].y = self.v[TR].y
+               
+               self.v[BR].x = self.v[TR].x
+               self.v[BR].y = self.v[BL].y 
+               
+
+       def setLeft(self, lft=None):     
+               self.v[TR].x = lft + self.v[TR].x - self.v[BL].x
+               self.v[BL].x = lft
+               # update othere verts
+               self.updateV34()
+
+       def setRight(self, rgt=None):    
+               self.v[BL].x = rgt - (self.v[TR].x - self.v[BL].x)
+               self.v[TR].x = rgt
+               self.updateV34()
+
+       def setBottom(self, btm=None):     
+               self.v[TR].y = btm + self.v[TR].y - self.v[BL].y
+               self.v[BL].y = btm
+               self.updateV34()
+
+       def setTop(self, tp=None):    
+               self.v[BL].y = tp - (self.v[TR].y - self.v[BL].y)
+               self.v[TR].y = tp
+               self.updateV34()
+                       
+       def getLeft(self):
+               return self.v[BL].x
+
+       def getRight(self):
+               return self.v[TR].x
+
+       def getBottom(self):
+               return self.v[BL].y
+
+       def getTop(self):
+               return self.v[TR].y
+       
+       # Returns none, meaning it didnt overlap any new boxes
+       def overlapAll(self, boxLs, intersectCache): # Flag index lets us know which quadere
+               if self.v[BL].x < 0:
+                       return None
+               elif self.v[BL].y < 0:
+                       return None                     
+               else:
+                       bIdx = len(intersectCache)
+                       while bIdx:
+                               bIdx-=1
+                               b = intersectCache[bIdx]
+                               if not (self.v[TR].y <= b.v[BL].y or\
+                                                               self.v[BL].y >= b.v[TR].y or\
+                                                               self.v[BL].x >= b.v[TR].x or\
+                                                               self.v[TR].x <= b.v[BL].x ):
+                                       
+                                       return None # Intersection with existing box
+                       #return 0 # Must keep looking
+                       
+                       
+                       for b in boxLs.boxes:
+                               if not (self.v[TR].y <= b.v[BL].y or\
+                                                               self.v[BL].y >= b.v[TR].y or\
+                                                               self.v[BL].x >= b.v[TR].x or\
+                                                               self.v[TR].x <= b.v[BL].x ):
+                                       
+                                       return b # Intersection with new box.
+                       return 0
+       
+       
+       
+       def place(self, vert, quad):
+               if quad == BLF:
+                       self.setLeft(vert.x)
+                       self.setBottom(vert.y)
+                       
+               elif quad == TRF:
+                       self.setRight(vert.x)
+                       self.setBottom(vert.y)
+               
+               elif quad == TLF:
+                       self.setLeft(vert.x)
+                       self.setTop(vert.y)
+
+               elif quad == BRF:
+                       self.setRight(vert.x)
+                       self.setTop(vert.y)
+       
+       # Trys to lock a box onto another box's verts
+       # cleans up double verts after 
+       def tryVert(self, boxes, baseVert):
+               flagIndex = -1
+               for freeQuad in quadFlagLs:
+                       flagIndex +=1
+                       #print 'Testing ', self.width
+                       if not baseVert.free & freeQuad:
+                               continue
+                       
+                       self.place(baseVert, freeQuad)
+                       overlapBox = self.overlapAll(boxes, baseVert.intersectCache[flagIndex])
+                       if overlapBox == 0: # There is no overlap
+                               baseVert.free &= ~freeQuad # Removes quad
+                               # Appends all verts but the one that matches. this removes the need for remove doubles
+                               for vIdx in range(4): # (BL,TR,TL,BR): # (BL,TR,TL,BR) / 0,1,2,3
+                                       self_v = self.v[vIdx] # shortcut
+                                       if not (self_v.x == baseVert.x and self_v.y == baseVert.y):
+                                               boxList.packedVerts.verts.append(self_v)
+                                       else:
+                                               baseVert.free &= self.v[vIdx].free # make sure the 
+                                               
+                                               # Inherit used boxes from old verts
+                                               if self_v.blb: baseVert.blb = self_v.blb 
+                                               if self_v.brb: baseVert.brb = self_v.brb #print 'inherit2'
+                                               if self_v.tlb: baseVert.tlb = self_v.tlb #print 'inherit3'
+                                               if self_v.trb: baseVert.trb = self_v.trb #print 'inherit4'
+                                               self.v[vIdx] = baseVert
+                               
+                               
+                               # ========================== WHY DOSENT THIS WORK???
+                               #~ if baseVert.tlb and baseVert.trb:
+                                       #~ if self == baseVert.tlb or self == baseVert.trb:
+                                               
+                                               #~ if baseVert.tlb.height > baseVert.trb.height:
+                                                       #~ #baseVert.trb.v[TL].free &= ~TLF & ~BLF
+                                                       #~ baseVert.trb.v[TL].free &= ~TLF
+                                                       #~ baseVert.trb.v[TL].free &= ~BLF
+                                               
+                                               #~ elif baseVert.tlb.height < baseVert.trb.height:
+                                                       #~ #baseVert.trb.v[TL].free &= ~TLF & ~BLF
+                                                       #~ baseVert.tlb.v[TR].free &= ~TRF
+                                                       #~ baseVert.tlb.v[TR].free &= ~BRF
+                                               #~ else: # same
+                                                       #~ baseVert.tlb.v[TR].free &= ~BLF
+                                                       #~ baseVert.trb.v[TL].free &= ~BRF                                              
+                                                       
+                               
+                               #~ if baseVert.blb and baseVert.brb:
+                                       #~ if self == baseVert.blb or self == baseVert.brb:
+                                               
+                                               #~ if baseVert.blb.height > baseVert.brb.height:
+                                                       #~ #baseVert.trb.v[TL].free &= ~TLF & ~BLF
+                                                       #~ baseVert.brb.v[BL].free &= ~TLF
+                                                       #~ baseVert.brb.v[BL].free &= ~BLF
+                                               
+                                               #~ elif baseVert.blb.height < baseVert.brb.height:
+                                                       #~ #baseVert.trb.v[TL].free &= ~TLF & ~BLF
+                                                       #~ baseVert.blb.v[BR].free &= ~TRF
+                                                       #~ baseVert.blb.v[BR].free &= ~BRF
+                                               #~ else: # same
+                                                       #~ baseVert.blb.v[BR].free &= ~TLF
+                                                       #~ baseVert.brb.v[BL].free &= ~TRF              
+
+                                                       #~ # print 'Hay', baseVert.tlb.height, baseVert.trb.height
+                                               
+                               
+                               
+                               return 1 # Working
+                               
+                       # We have a box that intersects that quadrent.
+                       elif overlapBox != None:  # None is used for a box thats alredt in the freq list.
+                               
+                               # There was an overlap, add this box to the verts list
+                               #quadFlagLs = (BLF,BRF,TLF,TRF)
+                               baseVert.intersectCache[flagIndex].append(overlapBox)
+               return 0
+
+
+class boxList:
+       # Global vert pool, stores used lists
+       packedVerts = vertList() # will be vertList()
+       
+       def __init__(self, boxes):
+               self.boxes = boxes
+               
+               # keep a running update of the width and height so we know the area
+               # initialize with first box, fixes but where we whwere only packing 1 box
+               self.width = 0
+               self.height = 0
+               if len(boxes) > 0:
+                       for b in boxes:
+                               self.width = max(self.width, b.width)
+                               self.height = max(self.height, b.height)
+
+               
+               
+               
+               # boxArea is the total area of all boxes in the list,
+               # can be used with packArea() to determine waistage.
+               self.boxArea = 0 # incremented with addBox()
+               
+       
+       # Just like MyBoxLs.boxes.append(), but sets bounds 
+       def addBoxPack(self, box):
+               # Resize this boxlist bounds for the current box.
+               self.width = max(self.width, box.getRight())
+               self.height = max(self.height, box.getTop())
+               
+               self.boxArea += box.area
+               
+               
+               
+               # iterate through these
+               #~ quadFlagLs = (1,8,4,2) 
+               #~ # Flags for vert idx used quads
+               #~ BLF = 1; TRF = 2; TLF = 4; BRF = 8
+               #~ quadFlagLs = (BLF,BRF,TLF,TRF)
+               
+               # Look through all the free vert quads and see if there are some we can remove
+               # buggy but dont know why???, dont use.
+               '''
+               for v in box.v:
+                       
+                       # Is my bottom being used.
+                       
+                       if v.free & BLF and v.free & BRF: # BLF and BRF
+                               for b in self.boxes:
+                                       if b.v[TR].y == v.y:
+                                               if b.v[TR].x > v.x:
+                                                       if b.v[BL].x < v.x:
+                                                               v.free &= ~BLF # Removes quad
+                                                               v.free &= ~BRF # Removes quad
+                               
+                               # Is my left being used.
+                       if v.free & BLF and v.free & TLF:
+                               for b in self.boxes:
+                                       if b.v[TR].x == v.x:
+                                               if b.v[TR].y > v.y:
+                                                       if b.v[BL].y < v.y:
+                                                               v.free &= ~BLF # Removes quad
+                                                               v.free &= ~TLF # Removes quad
+                               
+                       if v.free & TRF and v.free & TLF:
+                               # Is my top being used.
+                               for b in self.boxes:
+                                       if b.v[BL].y == v.y:
+                                               if b.v[TR].x > v.x:
+                                                       if b.v[BL].x < v.x:
+                                                               v.free &= ~TLF # Removes quad
+                                                               v.free &= ~TRF # Removes quad
+                                                               
+                               
+                               # Is my right being used.
+                       if v.free & TRF and v.free & BRF:
+                               for b in self.boxes:
+                                       if b.v[BL].x == v.x:
+                                               if b.v[TR].y > v.y:
+                                                       if b.v[BL].y < v.y:
+                                                               v.free &= ~BRF # Removes quad
+                                                               v.free &= ~TRF # Removes quad
+                                                               
+               '''
+               self.boxes.append(box)
+               
+               
+               
+       # Just like MyBoxLs.boxes.append(), but sets bounds 
+       def addBox(self, box):
+               self.boxes.append(box)          
+               self.boxArea += box.area
+
+       # The area of the backing bounds.
+       def packedArea(self):
+               return self.width * self.height
+               
+       # Sort boxes by area
+       # TODO REPLACE WITH SORT(LAMBDA(CMP...))
+       def sortArea(self):
+               self.boxes.sort(lambda A, B: cmp(B.area, A.area) ) # Reverse area sort
+       
+       # BLENDER only
+       def draw(self):
+               m = NMesh.GetRaw()
+               
+               
+               for b in self.boxes:
+                       z = min(b.width, b.height ) / max(b.width, b.height )
+                       #z =  b.farea
+                       #z=0
+                       f = NMesh.Face()
+                       m.verts.append(NMesh.Vert(b.getLeft(), b.getBottom(), z))
+                       f.v.append(m.verts[-1])
+                       m.verts.append(NMesh.Vert(b.getRight(), b.getBottom(), z))
+                       f.v.append(m.verts[-1])         
+                       m.verts.append(NMesh.Vert(b.getRight(), b.getTop(), z))         
+                       f.v.append(m.verts[-1])
+                       m.verts.append(NMesh.Vert(b.getLeft(), b.getTop(), z))
+                       f.v.append(m.verts[-1])
+                       m.faces.append(f)
+               NMesh.PutRaw(m, 's')    
+               Window.Redraw(1)
+       
+       def pack(self):
+               self.sortArea()
+               
+               if len(self.boxes) == 0:
+                       return
+                       
+               packedboxes = boxList([self.boxes[0]])
+               
+               # Remove verts we KNOW cant be added to
+               
+               unpackedboxes = boxList(self.boxes[1:])
+               
+               # STart with this box
+               boxList.packedVerts.verts.extend(packedboxes.boxes[0].v)
+               
+               while unpackedboxes.boxes != []:
+                       
+                       freeBoxIdx = 0
+                       while freeBoxIdx < len(unpackedboxes.boxes):
+                               
+                               # Sort the verts with this boxes dimensions as a bias, so less poky out bits are made.
+                               boxList.packedVerts.sortCorner(unpackedboxes.boxes[freeBoxIdx].width, unpackedboxes.boxes[freeBoxIdx].height)
+                               
+                               vertIdx = 0
+                               
+                               while vertIdx < len(boxList.packedVerts.verts):
+                                       baseVert = boxList.packedVerts.verts[vertIdx]
+                                       
+                                       if baseVert.free != 0:
+                                               # This will lock the box if its possibel
+                                               if unpackedboxes.boxes[freeBoxIdx].tryVert(packedboxes, baseVert):
+                                                       packedboxes.addBoxPack(unpackedboxes.boxes[freeBoxIdx])
+                                                       unpackedboxes.boxes.pop(freeBoxIdx) 
+                                                       freeBoxIdx = -1
+                                                       break
+                                               
+                                       vertIdx +=1
+                               freeBoxIdx +=1
+                               
+               boxList.packedVerts.verts = [] # Free the list, so it dosent use ram between runs.
+               
+               self.width = packedboxes.width
+               self.height = packedboxes.height
+       # All boxes as a list - X/Y/WIDTH/HEIGHT
+       def list(self):
+               return [(b.id, b.getLeft(), b.getBottom(), b.width, b.height ) for b in self.boxes]
+
+
+''' Define all globals here '''
+# vert IDX's, make references easier to understand.
+BL = 0; TR = 1; TL = 2; BR = 3
+
+# iterate through these
+# Flags for vert idx used quads
+BLF = 1; TRF = 2; TLF = 4; BRF = 8
+quadFlagLs = (BLF,BRF,TLF,TRF)
+
+
+# Packs a list w/h's into box types and places then #Iter times
+def boxPackIter(boxLs, iter=1, draw=0):
+       iterIdx = 0
+       bestArea = None
+       # Iterate over packing the boxes to get the best FIT!
+       while iterIdx < iter:
+               myBoxLs = boxList([])
+               for b in boxLs:
+                       myBoxLs.addBox( box(b[1], b[2], b[0]) ) # w/h/id
+               
+               myBoxLs.pack()
+               # myBoxLs.draw() # Draw as we go?
+               
+               newArea = myBoxLs.packedArea()
+               
+               #print 'pack test %s of %s, area:%.2f' % (iterIdx, iter, newArea)
+               
+               # First time?
+               if bestArea == None:
+                       bestArea = newArea
+                       bestBoxLs = myBoxLs
+               elif newArea < bestArea:
+                       bestArea = newArea
+                       bestBoxLs = myBoxLs
+               iterIdx+=1
+       
+       
+       if draw:
+               bestBoxLs.draw()
+       
+       #print 'best area: %.4f, %.2f%% efficient' % (bestArea, (float(bestBoxLs.boxArea) / (bestArea+0.000001))*100)
+
+       return bestBoxLs.width, bestBoxLs.height, bestBoxLs.list()
\ No newline at end of file
diff --git a/release/scripts/image_edit.py b/release/scripts/image_edit.py
new file mode 100644 (file)
index 0000000..17dc182
--- /dev/null
@@ -0,0 +1,95 @@
+#!BPY
+"""
+Name: 'Image Edit (External App)'
+Blender: 241
+Group: 'UV'
+Tooltip: 'Edits the image in another application. The Gimp for eg.'
+"""
+
+__author__ = "Campbell Barton"
+__url__ = ["blender", "elysiun"]
+__version__ = "1.0"
+
+__bpydoc__ = """\
+This script opens the current image in an external application for editing.
+
+Useage:
+Choose an image for editing in the UV/Image view.
+Select UVs, Image Edit (External App)
+For first time users try running the default application *
+If the application does not open you can type in the full path.
+The last entered application will be saved as a default.
+
+* Note, Start for win32 and open for macos will use system default assosiated application.
+"""
+
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+# --------------------------------------------------------------------------
+
+
+try:
+       import os
+       import sys as py_sys
+       platform = py_sys.platform
+except:
+       Draw.PupMenu('Error, python not installed')
+       os=None
+
+import Blender
+from Blender import Image, sys, Draw, Registry
+
+def main():
+       image = Image.GetCurrent()
+       if not image: # Image is None
+               print 'ERROR: You must select an active Image.'
+               return
+       imageFileName = sys.expandpath( image.filename )
+       
+       pupblock = [imageFileName.split('/')[-1].split('\\')[-1]]
+       
+       try:
+               appstring = Registry.GetKey('ExternalImageEditor', True)
+               appstring = appstring['path']
+       except:
+               pupblock.append('first time, set path.')
+               if platform == 'win32':
+                       appstring = 'start "%f"'
+               elif platform == 'darwin':
+                       appstring = 'open "%f"'
+               else:
+                       appstring = 'gimp-remote "%f"'
+       
+       appstring_but = Draw.Create(appstring)
+       
+       pupblock.append(('editor: ', appstring_but, 0, 48, 'Path to application, %f will be replaced with the image path.'))
+       
+       if not Draw.PupBlock('External Image Editor...', pupblock):
+               return
+       
+       appstring = appstring_but.val
+       
+       Registry.SetKey('ExternalImageEditor', {'path':appstring}, True)
+       
+       # -------------------------------
+       
+       appstring = appstring.replace('%f', imageFileName)
+       os.system(appstring)
+
+if __name__ == '__main__' and os != None:
+       main()
\ No newline at end of file
diff --git a/release/scripts/mesh_bbrush_menu.py b/release/scripts/mesh_bbrush_menu.py
new file mode 100644 (file)
index 0000000..e79e209
--- /dev/null
@@ -0,0 +1,44 @@
+#!BPY
+"""
+Name: 'B-Brush Sculpter'
+Blender: 240
+Group: 'Mesh'
+Tooltip: 'Sculpt the active mesh (adds a scriptlink)'
+"""
+
+import Blender
+def main():
+       name = 'mesh_bbrush.py'
+       for t in Blender.Text.Get():
+               if t.name.startswith(name):
+                       Blender.Draw.PupMenu('ERROR: Script "%s" alredy imported, aborting load.' % name)
+                       return
+       
+       # Load the text
+       datadir = Blender.Get('datadir')
+       if not datadir.endswith(Blender.sys.sep):
+               datadir += Blender.sys.sep
+       path= datadir + name
+       try:
+               t = Blender.Text.Load(path)
+       except:
+               Blender.Draw.PupMenu('ERROR: "%s" not found.' % path)
+               
+       pup_input = [\
+       'B-Brush Usage (message only)%t',
+       'Enable B-Brush by',
+       'selecting the "View" menu, then',
+       '"Space Handeler Scripts",',
+       '"%s"' % name,
+       'LMB to sculpt, RMB for prefs, Shift reverses pressure',
+       ]
+       
+       #Blender.Draw.PupBlock('%s loaded' % name ,pup_input)
+       Blender.Draw.PupMenu('|'.join(pup_input))
+       
+if __name__ == '__main__':
+
+       
+       
+       main()
+       
\ No newline at end of file
diff --git a/release/scripts/mesh_cleanup.py b/release/scripts/mesh_cleanup.py
new file mode 100644 (file)
index 0000000..09b1ef7
--- /dev/null
@@ -0,0 +1,184 @@
+#!BPY
+"""
+Name: 'Clean meshes'
+Blender: 228
+Group: 'Mesh'
+Tooltip: 'Clean unused data from all selected mesh objects.'
+"""
+from Blender import *
+from Blender.Mathutils import TriangleArea
+
+def rem_free_verts(me):
+       vert_users = [0] * len(me.verts)
+       for f in me.faces:
+               for v in f.v:
+                       vert_users[v.index]+=1
+       
+       for e in me.edges:
+               for v in e: # loop on edge verts
+                       vert_users[v.index]+=1
+       
+       verts_free = []
+       for i, users in enumerate(vert_users):
+               if not users:
+                       verts_free.append(i)
+       
+       if verts_free:
+               pass
+               me.verts.delete(verts_free)
+       return len(verts_free)
+       
+def rem_free_edges(me, limit=None):
+       ''' Only remove based on limit if a limit is set, else remove all '''
+       def sortPair(a,b):
+               return min(a,b), max(a,b)
+       
+       edgeDict = {} # will use a set when python 2.4 is standard.
+       
+       for f in me.faces:
+               for i in xrange(len(f.v)):
+                       edgeDict[sortPair(f.v[i].index, f.v[i-1].index)] = None
+       
+       edges_free = []
+       for e in me.edges:
+               if not edgeDict.has_key(sortPair(e.v1.index, e.v2.index)):
+                       edges_free.append(e)
+       
+       if limit != None:
+               edges_free = [e for e in edges_free if (e.v1.co-e.v2.co).length <= limit]
+       
+       me.edges.delete(edges_free)
+       return len(edges_free)
+
+def rem_area_faces(me, limit=0.001):
+       ''' Faces that have an area below the limit '''
+       def faceArea(f):
+               if len(f.v) == 3:
+                       return TriangleArea(f.v[0].co, f.v[1].co, f.v[2].co)
+               elif len(f.v) == 4:
+                       return\
+                        TriangleArea(f.v[0].co, f.v[1].co, f.v[2].co) +\
+                        TriangleArea(f.v[0].co, f.v[2].co, f.v[3].co)
+       rem_faces = [f for f in me.faces if faceArea(f) <= limit]
+       if rem_faces:
+               me.faces.delete( 0, rem_faces )
+       return len(rem_faces)
+
+def rem_perimeter_faces(me, limit=0.001):
+       ''' Faces whos combine edge length is below the limit '''
+       def faceEdLen(f):
+               if len(f.v) == 3:
+                       return\
+                       (f.v[0].co-f.v[1].co).length +\
+                       (f.v[1].co-f.v[2].co).length +\
+                       (f.v[2].co-f.v[0].co).length
+               elif len(f.v) == 4:
+                       return\
+                       (f.v[0].co-f.v[1].co).length +\
+                       (f.v[1].co-f.v[2].co).length +\
+                       (f.v[2].co-f.v[3].co).length +\
+                       (f.v[3].co-f.v[0].co).length
+       rem_faces = [f for f in me.faces if faceEdLen(f) <= limit]
+       if rem_faces:
+               me.faces.delete( 0, rem_faces )
+       return len(rem_faces)
+
+def main():
+       
+       def getLimit(text):
+               return Draw.PupFloatInput(text, 0.001, 0.0, 1.0, 0.1, 4)
+       
+       
+       scn = Scene.GetCurrent()
+       obsel = Object.GetSelected()
+       actob = scn.getActiveObject()
+       
+       is_editmode = Window.EditMode()
+       
+       # Edit mode object is not active, add it to the list.
+       if is_editmode and (not actob.sel):
+               obsel.append(actob)
+       
+       meshes = [ob.getData(mesh=1) for ob in obsel if ob.getType() == 'Mesh']
+       
+       
+       #====================================#
+       # Popup menu to select the functions #
+       #====================================#
+       '''
+       if not meshes:
+               Draw.PupMenu('ERROR%t|no meshes in selection')
+               return
+       method = Draw.PupMenu("""
+Clean Mesh, Remove...%t|
+Verts: free standing|
+Edges: not in a face|
+Edges: below a length|
+Faces: below an area|%l|
+All of the above|""")
+       if method == -1:
+               return
+       
+       if method >= 3:
+               limit = getLimit('threshold: ')
+       
+       print 'method', method
+       '''
+       
+       
+       CLEAN_VERTS_FREE = Draw.Create(1)
+       CLEAN_EDGE_NOFACE = Draw.Create(0)
+       CLEAN_EDGE_SMALL = Draw.Create(0)
+       CLEAN_FACE_PERIMETER = Draw.Create(0)
+       CLEAN_FACE_SMALL = Draw.Create(0)
+       limit = Draw.Create(0.01)
+       
+       # Get USER Options
+       
+       pup_block = [\
+       ('Verts: free', CLEAN_VERTS_FREE, 'Remove verts that are not used by an edge or a face.'),\
+       ('Edges: free', CLEAN_EDGE_NOFACE, 'Remove edges that are not in a face.'),\
+       ('Edges: short', CLEAN_EDGE_SMALL, 'Remove edges that are below the length limit.'),\
+       ('Faces: small perimeter', CLEAN_FACE_PERIMETER, 'Remove faces below the perimeter limit.'),\
+       ('Faces: small area', CLEAN_FACE_SMALL, 'Remove faces below the area limit (may remove faces stopping T-face artifacts).'),\
+       ('limit: ', limit, 0.001, 1.0, 'Limit used for the area and length tests above (a higher limit will remove more data).'),\
+       ]
+       
+       
+       if not Draw.PupBlock('Clean Selected Meshes...', pup_block):
+               return
+       
+       
+       CLEAN_VERTS_FREE = CLEAN_VERTS_FREE.val
+       CLEAN_EDGE_NOFACE = CLEAN_EDGE_NOFACE.val
+       CLEAN_EDGE_SMALL = CLEAN_EDGE_SMALL.val
+       CLEAN_FACE_PERIMETER = CLEAN_FACE_PERIMETER.val
+       CLEAN_FACE_SMALL = CLEAN_FACE_SMALL.val
+       limit = limit.val
+       
+       if is_editmode: Window.EditMode(0)
+       
+       rem_face_count = rem_edge_count = rem_vert_count = 0
+       
+       for me in meshes:
+               if CLEAN_FACE_SMALL:
+                       rem_face_count += rem_area_faces(me, limit)
+                       
+               if CLEAN_FACE_PERIMETER:
+                       rem_face_count += rem_perimeter_faces(me, limit)
+               
+               if CLEAN_EDGE_SMALL: # for all use 2- remove all edges.
+                       rem_edge_count += rem_free_edges(me, limit)
+               
+               if CLEAN_EDGE_NOFACE:
+                       rem_edge_count += rem_free_edges(me)
+               
+               if CLEAN_VERTS_FREE:
+                       rem_vert_count += rem_free_verts(me)
+       
+       if is_editmode: Window.EditMode(0)
+       Draw.PupMenu('Removed from ' + str(len(meshes)) +' Mesh(es)%t|' + 'Verts:' + str(rem_vert_count) + ' Edges:' + str(rem_edge_count) + ' Faces:' + str(rem_face_count))
+       
+if __name__ == '__main__':
+       main()
+       
diff --git a/release/scripts/mesh_tri2quad.py b/release/scripts/mesh_tri2quad.py
new file mode 100644 (file)
index 0000000..4a73418
--- /dev/null
@@ -0,0 +1,453 @@
+#!BPY
+
+"""
+Name: 'Triangles to Quads'
+Blender: 240
+Group: 'Mesh'
+Tooltip: 'Triangles to Quads for all selected mesh objects.'
+"""
+
+__author__ = "Campbell Barton AKA Ideasman"
+__url__ = ["http://members.iinet.net.au/~cpbarton/ideasman/", "blender", "elysiun"]
+
+__bpydoc__ = """\
+This script joins any triangles into quads for all selected mesh objects.
+
+Usage:
+
+Select the mesh(es) and run this script. Mesh data will be edited in place.
+so make a backup copy first if your not sure of the results
+
+The limit value allows you to choose how pedantic the algorithum is when detecting errors between 2 triangles.
+Over 50 could result in quads that are not correct.
+
+The joining of quads takes into account UV mapping, UV Images and Vertex colours
+and will not join faces that have mis-matching data.
+"""
+
+
+from Blender import Object, Mathutils, Draw, Window, sys
+import math
+
+TRI_LIST = (0,1,2)
+
+vecAngle = Mathutils.AngleBetweenVecs
+TriangleNormal = Mathutils.TriangleNormal
+
+#=============================================================================#
+# All measurement algorithums for face compatibility when joining into quads                                    #
+# every function returns a value between 0.0 and 1.0                                                                                                                                    #
+#=============================================================================#
+# total diff is 1.0, no diff is 0.0
+# measure accross 2 possible triangles in the imagined quad.
+def isfaceNoDiff(imagQuag):
+       # Divide the quad one way and measure normals
+       noA1 = TriangleNormal(imagQuag[0].co, imagQuag[1].co, imagQuag[2].co)
+       noA2 = TriangleNormal(imagQuag[0].co, imagQuag[2].co, imagQuag[3].co)
+       
+       if noA1 == noA2:
+               normalADiff = 0.0
+       else:
+               try:
+                       normalADiff = vecAngle(noA1, noA2)
+               except:
+                       #print noA1, noA2
+                       normalADiff = 179
+               
+       #print normalADiff, noA1, noA2
+       
+       # Alternade division of the quad
+       noB1 = TriangleNormal(imagQuag[1].co, imagQuag[2].co, imagQuag[3].co)
+       noB2 = TriangleNormal(imagQuag[3].co, imagQuag[0].co, imagQuag[1].co)   
+       if noB1 == noB2:
+               normalBDiff = 0.0
+       else:
+               try:
+                       normalBDiff = vecAngle(noB1, noB2)
+               except:
+                       # print noB1, noB2
+                       normalBDiff = 179
+       
+       # Should never be NAN anymore.
+       '''
+       if normalBDiff != normalBDiff or normalADiff != normalADiff:
+               raise "NAN"
+       '''
+       # The greatest possible difference is 180 for each
+       return (normalADiff/360) + (normalBDiff/360)
+
+
+# 4 90d angles == 0, each corner diff from 90 is added together.
+# 360 is total possible difference,
+def isfaceCoLin(imagQuag): 
+       
+       edgeVec1 = imagQuag[0].co - imagQuag[1].co
+       edgeVec2 = imagQuag[1].co - imagQuag[2].co
+       edgeVec3 = imagQuag[2].co - imagQuag[3].co
+       edgeVec4 = imagQuag[3].co - imagQuag[0].co
+       
+       # Work out how different from 90 each edge is.
+       diff = 0
+       try:
+               diff += abs(vecAngle(edgeVec1, edgeVec2) - 90)
+               diff += abs(vecAngle(edgeVec2, edgeVec3) - 90)
+               diff += abs(vecAngle(edgeVec3, edgeVec4) - 90)
+               diff += abs(vecAngle(edgeVec4, edgeVec1) - 90)
+       
+       except:
+               return 1.0
+       
+       # Avoid devide by 0
+       if not diff:
+               return 0.0
+       
+       return diff/360
+
+
+# Meause the areas of the 2 possible ways of subdividing the imagined quad.
+# if 1 is very different then the quad is concave.
+# We should probably throw out any pairs that are at all concave,
+# since even a slightly concacve paor will definetly be co linear.
+# though even virging on this can be a recipe for a bad join.
+def isfaceConcave(imagQuag):
+       # Add the 2 areas the deviding one way
+       areaA =\
+       Mathutils.TriangleArea(imagQuag[0].co, imagQuag[1].co, imagQuag[2].co) +\
+       Mathutils.TriangleArea(imagQuag[0].co, imagQuag[2].co, imagQuag[3].co)
+       
+       # Add the tri's triangulated the alternate way.
+       areaB =\
+       Mathutils.TriangleArea(imagQuag[1].co, imagQuag[2].co, imagQuag[3].co) +\
+       Mathutils.TriangleArea(imagQuag[3].co, imagQuag[0].co, imagQuag[1].co)
+       
+       # Make a ratio of difference so they are between 1 and 0
+       # Need to invert the value so 1.0 is 0
+       minarea = min(areaA, areaB)
+       maxarea = max(areaA, areaB)
+       
+       # Aviod devide by 0
+       if maxarea == 0.0:
+               return 1
+       else:
+               return 1 - (minarea / maxarea)
+
+
+
+# This returns a list of verts, to test
+# dosent modify the actual faces.
+def meshJoinFacesTest(f1, f2, V1FREE, V2FREE): 
+       # pretend face
+       dummyFace = f1.v[:]
+       
+       # We know the 2 free verts. insert the f2 free vert in 
+       if V1FREE is 0:
+               dummyFace.insert(2, f2.v[V2FREE])
+       elif V1FREE is 1:
+               dummyFace.append(f2.v[V2FREE])
+       elif V1FREE is 2:
+               dummyFace.insert(1, f2.v[V2FREE])
+       
+       return dummyFace
+
+
+# Measure how good a quad the 2 tris will make,
+def measureFacePair(f1, f2, f1free, f2free):
+       # Make a imaginary quad. from 2 tris into 4 verts
+       imagFace = meshJoinFacesTest(f1, f2, f1free, f2free)
+       if imagFace is False:
+               return False
+       
+       measure = 0 # start at 0, a lower value is better.
+       
+       # Do a series of tests,
+       # each value will add to the measure value
+       # and be between 0 and 1.0
+       
+       measure+= isfaceNoDiff(imagFace)
+       measure+= isfaceCoLin(imagFace)
+       measure+= isfaceConcave(imagFace)
+       
+       # For debugging.
+       '''
+       a1= isfaceNoDiff(imagFace)
+       a2= isfaceCoLin(imagFace)
+       a3= isfaceConcave(imagFace)
+       
+       print 'a1 %f' % a1
+       print 'a2 %f' % a2
+       print 'a3 %f' % a3
+       
+       measure = a1+a2+a3
+       '''
+       
+       return [f1,f2, measure/3, f1free, f2free]
+
+
+# We know the faces are good to join, simply execute the join
+# by making f1 into a quad and f2 inde an edge (2 vert face.)
+INSERT_LOOKUP = (2,3,1)
+OTHER_LOOKUP = ((1,2),(0,2),(0,1))
+def meshJoinFaces(f1, f2, V1FREE, V2FREE, mesh):
+       
+       # Only used if we have edges.
+       # DEBUG
+       edgeVert1, edgeVert2 = OTHER_LOOKUP[V1FREE]
+       edgeVert1, edgeVert2 = f1[edgeVert1], f1[edgeVert2]
+       
+       
+       fverts = f1.v[:]
+       if mesh.hasFaceUV():
+               fuvs = f1.uv[:]
+       if f1.col:
+               fcols = f1.col[:]
+               
+       
+       # We know the 2 free verts. insert the f2 free vert in 
+       # Work out which vert to insert
+       i = INSERT_LOOKUP[V1FREE]
+       
+       # Insert the vert in the desired location.
+       fverts.insert(i, f2.v[V2FREE])
+       if mesh.hasFaceUV():
+               fuvs.insert(i, f2.uv[V2FREE])
+       if f1.col:
+               fcols.insert(i, f2.col[V2FREE])         
+       
+       # Assign the data to the faces.
+       f1.v = fverts
+       if mesh.hasFaceUV():
+               f1.uv = fuvs
+       if f1.col:
+               f1.col = fcols
+       
+       # Make an edge from the 2nd vert.
+       # removing anything other then the free vert will
+       # remove the edge from accress the new quad
+       f2.v.pop(not V2FREE)
+       
+       
+       # DEBUG
+       if mesh.edges:
+               mesh.removeEdge(edgeVert1, edgeVert2)
+               
+       #return f2
+
+
+
+def compare2(v1, v2, limit):
+       if v1[0] + limit > v2[0] and v1[0] - limit < v2[0] and\
+       v1[1] + limit > v2[1] and v1[1] - limit < v2[1]:
+                               return True
+       return False
+
+
+def compare3(v1, v2, limit):
+       if v1[0] + limit > v2[0] and v1[0] - limit < v2[0] and\
+       v1[1] + limit > v2[1] and v1[1] - limit < v2[1] and\
+       v1[2] + limit > v2[2] and v1[2] - limit < v2[2]:
+               return True
+       return False
+
+
+UV_LIMIT = 0.005 # 0.0 to 1.0, can be greater then these bounds tho
+def compareFaceUV(f1, f2):
+       if f1.image == None and f1.image == None:
+               # No Image, ignore uv's
+               return True
+       elif f1.image != f2.image:
+               # Images differ, dont join faces.
+               return False
+       
+       # We know 2 of these will match.
+       for v1i in TRI_LIST:
+               for v2i in TRI_LIST:
+                       if f1[v1i] is f2[v2i]:
+                               # We have a vertex index match.
+                               # now match the UV's
+                               if not compare2(f1.uv[v1i], f2.uv[v2i], UV_LIMIT):
+                                       # UV's are different
+                                       return False
+       
+       return True
+
+
+COL_LIMIT = 3 # 0 to 255
+def compareFaceCol(f1, f2):
+       # We know 2 of these will match.
+       for v1i in TRI_LIST:
+               for v2i in TRI_LIST:
+                       if f1[v1i] is f2[v2i]:
+                               # We have a vertex index match.
+                               # now match the UV's
+                               if not compare3(f1.col[v1i], f2.col[v2i], COL_LIMIT):
+                                       # UV's are different
+                                       return False
+                                       
+       return True     
+
+def sortPair(a,b):
+       return min(a,b), max(a,b)
+
+def tri2quad(mesh, limit, selectedFacesOnly):
+       print '\nStarting tri2quad for mesh: %s' % mesh.name
+       print '\t...finding pairs'
+       time1 = sys.time()      # Time the function
+       pairCount = 0
+       
+       # each item in this list will be a list
+       # [face1, face2, measureFacePairValue]
+       facePairLs = [] 
+       
+       if selectedFacesOnly:
+               faceList = [f for f in mesh.faces if f.sel if len(f) is 3 if not f.hide]
+       else:
+               faceList = [f for f in mesh.faces if len(f) == 3]
+       
+       has_face_uv = mesh.hasFaceUV()
+       has_vert_col = mesh.hasVertexColours() 
+       
+       
+       # Build a list of edges and tris that use those edges.
+       edgeFaceUsers = {}
+       for f in faceList:
+               i1,i2,i3 = f.v[0].index, f.v[1].index, f.v[2].index
+               ed1, ed2, ed3 = sortPair(i1, i2), sortPair(i2, i3), sortPair(i3, i1)
+               
+               # The second int in the tuple is the free vert, its easier to store then to work it out again.
+               try: edgeFaceUsers[ed1].append((f, 2))
+               except: edgeFaceUsers[ed1] = [(f, 2)]
+               
+               try: edgeFaceUsers[ed2].append((f, 0))
+               except: edgeFaceUsers[ed2] = [(f, 0)]
+               
+               try: edgeFaceUsers[ed3].append((f, 1))
+               except: edgeFaceUsers[ed3] = [(f, 1)]
+       
+       
+       edgeDoneCount = 0
+       for faceListEdgeShared in edgeFaceUsers.itervalues():
+               if len(faceListEdgeShared) == 2:
+                       f1, f1free = faceListEdgeShared[0]
+                       f2, f2free = faceListEdgeShared[1]
+                       
+                       if f1.mat != f2.mat:
+                               pass # faces have different materials.
+                       elif has_face_uv and (not compareFaceUV(f1, f2)):
+                               pass # UV's are there but dont match.
+                       elif has_vert_col and not compareFaceCol(f1, f2):
+                               pass # Colours are there but dont match.
+                       else:
+                               # We can now store the qpair and measure
+                               # there eligability for becoming 1 quad.
+                               pair = measureFacePair(f1, f2, f1free, f2free)
+                               if pair is not False and pair[2] < limit: # Some terraible error
+                                       facePairLs.append(pair)
+                                       pairCount += 1
+                       
+                       edgeDoneCount += 1
+                       if not edgeDoneCount % 20:
+                               p = float(edgeDoneCount) / len(edgeFaceUsers)
+                               Window.DrawProgressBar(p*0.5, 'Found pairs: %i' % pairCount)
+       
+       
+       # Sort, best options first :)
+       facePairLs.sort(lambda a,b: cmp(a[2], b[2]))
+       draws = 0
+       print '\t...joining pairs'
+       joinCount = 0
+       len_facePairLs = len(facePairLs)
+       
+       #faces_to_remove = []
+       
+       for pIdx, pair in enumerate(facePairLs):
+               # We know the last item is the best option, and no other face pairs will get in the way.
+               # now join the faces.
+               
+               # If any of the faces have alredy been used then they will not have a lengh of 3 verts
+               if len(pair[0]) is 3 and len(pair[1]) is 3:
+                       joinCount +=1
+                       # print 'joining faces', joinCount, 'Limit:', facePairLs[-1][2]
+                       #faces_to_remove.append( meshJoinFaces(pair[0], pair[1], mesh) )
+                       meshJoinFaces(pair[0], pair[1], pair[3], pair[4], mesh)
+                       
+                       if not pIdx % 20:
+                               p = (0.5 + ((float((len_facePairLs - (len_facePairLs - pIdx))))/len_facePairLs*0.5)) * 0.99
+                               Window.DrawProgressBar(p, 'Joining Face count: %i' % joinCount)
+                               draws +=1
+                               
+       # print 'Draws', draws
+       
+       # Remove faces, due to a bug in ZR's new BF-Blender CVS
+       
+       fIdx = len(mesh.faces)
+       while fIdx:
+               fIdx -=1
+               if len(mesh.faces[fIdx]) < 3:
+                       mesh.faces.pop(fIdx)
+       
+       # Was buggy in 2.4.alpha fixed now I think
+       #mesh.faces[0].sel = 0
+       
+       
+       if joinCount:
+               print "tri2quad time for %s: %s joined %s tri's into quads" % (mesh.name, sys.time()-time1, joinCount)
+               
+               #mesh.update(0, (mesh.edges != []), 0)
+               mesh.update(0, 0, 0)
+               
+       else:
+               print "tri2quad nothing done %s: %s     joined none" % (mesh.name, sys.time()-time1)
+               
+
+
+#====================================#
+# Sanity checks                      #
+#====================================#
+def error(str):
+       Draw.PupMenu('ERROR%t|'+str)
+
+def main():
+       #selection = Object.Get()
+       selection = Object.GetSelected()
+       if len(selection) is 0:
+               error('No object selected')
+               return
+
+       #       GET UNIQUE MESHES.
+       meshDict = {}
+       # Mesh names
+       for ob in selection:
+               if ob.getType() == 'Mesh':
+                       meshDict[ob.getData(1)] = ob # dont do doubles.
+       
+       # Create the variables.
+       selectedFacesOnly = Draw.Create(1)
+       limit = Draw.Create(25)
+       
+       
+       pup_block = [\
+       ('Selected Faces Only', selectedFacesOnly, 'Use only selected faces from all selected meshes.'),\
+       ('Limit: ', limit, 1, 100, 'A higher value will join more tris to quads, even if the quads are not perfect.'),\
+       ]
+       selectedFacesOnly = selectedFacesOnly.val
+       limit = limit.val
+       
+       if not Draw.PupBlock('Tri2Quad for %i mesh object(s)' % len(meshDict), pup_block):
+               return  
+       
+       # We now know we can execute
+       is_editmode = Window.EditMode()
+       if is_editmode: Window.EditMode(0)
+       
+       limit = limit/100.0 # Make between 1 and 0
+       
+       for ob in meshDict.itervalues():
+               mesh = ob.getData()
+               tri2quad(mesh, limit, selectedFacesOnly)
+       if is_editmode: Window.EditMode(1)
+
+# Dont run when importing
+if __name__ == '__main__':
+       Window.DrawProgressBar(0.0, 'Triangles to Quads 1.1 ')
+       main()
+       Window.DrawProgressBar(1.0, '')
\ No newline at end of file
diff --git a/release/scripts/object_batch_name_edit.py b/release/scripts/object_batch_name_edit.py
new file mode 100644 (file)
index 0000000..f5cec15
--- /dev/null
@@ -0,0 +1,253 @@
+#!BPY
+
+"""
+Name: 'Batch Object Name Edit'
+Blender: 240
+Group: 'Object'
+Tooltip: 'Apply the chosen rule to rename all selected objects at once.'
+"""
+
+__author__ = "Campbell Barton"
+__url__ = ("blender", "elysiun")
+__version__ = "1.0"
+
+__bpydoc__ = """\
+"Batch Object Name Edit" allows you to change multiple names of Blender
+objects at once.  It provides options to define if you want to: replace text
+in the current names, truncate their beginnings or endings or prepend / append
+strings to them.
+
+Usage:
+
+Select the objects to be renamed and run this script from the Object->Scripts
+menu of the 3d View.
+"""
+
+
+# $Id$
+#
+# --------------------------------------------------------------------------
+# Batch Name Edit by Campbell Barton (AKA Ideasman)
+# --------------------------------------------------------------------------
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+# --------------------------------------------------------------------------
+
+from Blender import *
+
+global renameCount
+renameCount = 0
+
+def main():
+       global renameCount
+       # Rename the datablocks that are used by the object.
+       def renameLinkedDataFromObject():
+               
+               # Result 1, we want to rename data
+               for ob in Object.GetSelected():
+                       if ob.name == ob.getData(1):
+                               return # Alredy the same name, dont bother.
+                               
+                       try:
+                               if ob.getType() == 'Mesh': 
+                                       data = ob.getData(mesh=1) # use mesh so we dont have to update the nmesh.
+                               else:
+                                       data = ob.getData()
+                               data.name = ob.name
+                       except:
+                               # Maybe trying to renasme an empty, dont worry about this.
+                               pass
+               
+       
+       
+       def new():
+               global renameCount
+               NEW_NAME_STRING = Draw.Create('')
+               RENAME_LINKED = Draw.Create(0)
+               pup_block = [\
+               ('New Name: ', NEW_NAME_STRING, 19, 19, 'New Name'),\
+               ('Rename ObData', RENAME_LINKED, 'Renames objects data to match the obname'),\
+               ]
+               
+               if not Draw.PupBlock('Replace in name...', pup_block):
+                       return 0
+               
+               NEW_NAME_STRING= NEW_NAME_STRING.val
+               
+               Window.WaitCursor(1)
+               for ob in Object.GetSelected():
+                       if ob.name != NEW_NAME_STRING:
+                               ob.name = NEW_NAME_STRING
+                               renameCount+=1
+               
+               return RENAME_LINKED.val
+               
+       def replace():
+               global renameCount
+               REPLACE_STRING = Draw.Create('')
+               WITH_STRING = Draw.Create('')
+               RENAME_LINKED = Draw.Create(0)
+               
+               pup_block = [\
+               ('Replace: ', REPLACE_STRING, 19, 19, 'Text to find'),\
+               ('With:', WITH_STRING, 19, 19, 'Text to replace with'),\
+               ('Rename ObData', RENAME_LINKED, 'Renames objects data to match the obname'),\
+               ]
+               
+               if not Draw.PupBlock('Replace in name...', pup_block) or\
+               ((not REPLACE_STRING.val) and (not WITH_STRING)):
+                       return 0
+               
+               REPLACE_STRING = REPLACE_STRING.val
+               WITH_STRING = WITH_STRING.val
+               
+               Window.WaitCursor(1)
+               for ob in Object.GetSelected():
+                       newname = ob.name.replace(REPLACE_STRING, WITH_STRING)
+                       if ob.name != newname:
+                               ob.name = newname
+                               renameCount+=1
+               return RENAME_LINKED.val
+                       
+       
+       def prefix():
+               global renameCount
+               PREFIX_STRING = Draw.Create('')
+               RENAME_LINKED = Draw.Create(0)
+               
+               pup_block = [\
+               ('Prefix: ', PREFIX_STRING, 19, 19, 'Name prefix'),\
+               ('Rename ObData', RENAME_LINKED, 'Renames objects data to match the obname'),\
+               ]
+               
+               if not Draw.PupBlock('Prefix...', pup_block) or\
+               not PREFIX_STRING.val:
+                       return 0
+               
+               PREFIX_STRING = PREFIX_STRING.val
+               
+               Window.WaitCursor(1)
+               for ob in Object.GetSelected():
+                       ob.name = PREFIX_STRING + ob.name
+                       renameCount+=1 # we knows these are different.
+               return RENAME_LINKED.val
+               
+       def suffix():
+               global renameCount
+               SUFFIX_STRING = Draw.Create('')
+               RENAME_LINKED = Draw.Create(0)
+               
+               pup_block = [\
+               ('Suffix: ', SUFFIX_STRING, 19, 19, 'Name suffix'),\
+               ('Rename ObData', RENAME_LINKED, 'Renames objects data to match the obname'),\
+               ]
+               
+               if not Draw.PupBlock('Suffix...', pup_block) or\
+               not SUFFIX_STRING.val:
+                       return 0
+               
+               SUFFIX_STRING = SUFFIX_STRING.val
+               
+               Window.WaitCursor(1)
+               for ob in Object.GetSelected():
+                       ob.name =  ob.name + SUFFIX_STRING
+                       renameCount+=1 # we knows these are different.
+               return RENAME_LINKED.val
+       
+       def truncate_start():
+               global renameCount
+               TRUNCATE_START = Draw.Create(0)
+               RENAME_LINKED = Draw.Create(0)
+               
+               pup_block = [\
+               ('Truncate Start: ', TRUNCATE_START, 0, 19, 'Truncate chars from the start of the name'),\
+               ('Rename ObData', RENAME_LINKED, 'Renames objects data to match the obname'),\
+               ]
+               
+               if not Draw.PupBlock('Truncate Start...', pup_block) or\
+               not TRUNCATE_START.val:
+                       return 0
+                       
+               Window.WaitCursor(1)
+               TRUNCATE_START = TRUNCATE_START.val
+               for ob in Object.GetSelected():
+                       newname = ob.name[TRUNCATE_START: ]
+                       ob.name = newname
+                       renameCount+=1
+               
+               return RENAME_LINKED.val
+
+       
+       def truncate_end():
+               global renameCount
+               TRUNCATE_END = Draw.Create(0)
+               RENAME_LINKED = Draw.Create(0)
+               
+               pup_block = [\
+               ('Truncate End: ', TRUNCATE_END, 0, 19, 'Truncate chars from the end of the name'),\
+               ('Rename ObData', RENAME_LINKED, 'Renames objects data to match the obname'),\
+               ]
+               
+               if not Draw.PupBlock('Truncate End...', pup_block) or\
+               not TRUNCATE_END.val:
+                       return 0
+                       
+               Window.WaitCursor(1)
+               TRUNCATE_END = TRUNCATE_END.val
+               for ob in Object.GetSelected():
+                       newname = ob.name[: -TRUNCATE_END]
+                       ob.name = newname
+                       renameCount+=1
+               
+               return RENAME_LINKED.val
+
+       def renameObjectFromLinkedData():
+               global renameCount
+               Window.WaitCursor(1)
+               
+               for ob in Object.GetSelected():
+                       newname = ob.getData(1)
+                       if newname != None and ob.name != newname:
+                               ob.name = newname
+                               renameCount+=1
+               return 0
+               
+       
+       name = "Selected Object Names%t|New Name|Replace Text|Add Prefix|Add Suffix|Truncate Start|Truncate End|Rename Objects to Data Names"
+       result = Draw.PupMenu(name)
+       
+       renLinked = 0 # Rename linked data to the object name?
+       
+       if result == -1:
+               return
+       elif result == 1: renLinked= new()
+       elif result == 2: renLinked= replace()
+       elif result == 3: renLinked= prefix()
+       elif result == 4: renLinked= suffix()
+       elif result == 5: renLinked= truncate_start()
+       elif result == 6: renLinked= truncate_end()
+       elif result == 7: renameObjectFromLinkedData()
+       
+       if renLinked:
+               renameLinkedDataFromObject()
+       
+       Window.WaitCursor(0)
+       
+       Draw.PupMenu('renamed: %d objects.' % renameCount)
+
+main()
diff --git a/release/scripts/ply_export.py b/release/scripts/ply_export.py
new file mode 100644 (file)
index 0000000..8619769
--- /dev/null
@@ -0,0 +1,113 @@
+#!BPY
+
+"""
+Name: 'PLY...'
+Blender: 237
+Group: 'Export'
+Tooltip: 'Export to Stanford PLY format'
+"""
+
+import Blender
+import meshtools
+import math
+
+__author__ = "Bruce Merry"
+__version__ = "0.9"
+__bpydoc__ = """\
+This script exports Stanford PLY files from Blender. It supports per-vertex
+normals and per-face colours and texture coordinates.
+"""
+
+# Copyright (C) 2004, 2005: Bruce Merry, bmerry@cs.uct.ac.za
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+def file_callback(filename):
+        if filename.find('.ply', -4) < 0: filename += '.ply'
+        file = open(filename, "wb")
+        objects = Blender.Object.GetSelected()
+        obj = objects[0]
+        mesh = objects[0].data
+
+        have_uv = mesh.hasFaceUV()
+        have_col = meshtools.has_vertex_colors(mesh)
+        verts = [] # list of dictionaries
+        vdict = {} # (index, normal, uv) -> new index
+        for (i, f) in enumerate(mesh.faces):
+                for (j, v) in enumerate(f.v):
+                        index = v.index
+                        key = index, tuple(v.no)
+                        vdata = {'position': v.co, 'normal': v.no}
+                        if have_uv:
+                                vdata['uv'] = f.uv[j]
+                                key = key + (tuple(f.uv[j]), )
+                        if have_col:
+                                vdata['col'] = f.col[j]
+                                key = key + ((f.col[j].r, f.col[j].g, f.col[j].b, f.col[j].a), )
+                        if not vdict.has_key(key):
+                                vdict[key] = len(verts);
+                                verts.append(vdata)
+                if not i % 100 and meshtools.show_progress:
+                        Blender.Window.DrawProgressBar(float(i) / len(mesh.faces), "Organising vertices")
+
+        print >> file, "ply"
+        print >> file, "format ascii 1.0"
+        print >> file, "comment created by ply_export.py from Blender"
+        print >> file, "element vertex %d" % len(verts)
+        print >> file, "property float32 x"
+        print >> file, "property float32 y"
+        print >> file, "property float32 z"
+        print >> file, "property float32 nx"
+        print >> file, "property float32 ny"
+        print >> file, "property float32 nz"
+        if have_uv:
+                print >> file, "property float32 s"
+                print >> file, "property float32 t"
+        if have_col:
+                print >> file, "property uint8 red"
+                print >> file, "property uint8 green"
+                print >> file, "property uint8 blue"
+        print >> file, "element face %d" % len(mesh.faces)
+        print >> file, "property list uint8 int32 vertex_indices"
+        print >> file, "end_header"
+
+        for (i, v) in enumerate(verts):
+                print >> file, "%f %f %f %f %f %f" % (tuple(v['position']) + tuple(v['normal'])),
+                if have_uv: print >> file, "%f %f" % tuple(v['uv']),
+                if have_col: print >> file, "%u %u %u" % (v['col'].r, v['col'].g, v['col'].b),
+                print >> file
+                if not i % 100 and meshtools.show_progress:
+                        Blender.Window.DrawProgressBar(float(i) / len(verts), "Writing vertices")
+        for (i, f) in enumerate(mesh.faces):
+                print >> file, "%d" % len(f.v),
+                for j in range(len(f.v)):
+                        v = f.v[j]
+                        index = v.index
+                        key = index, tuple(v.no)
+                        if have_uv:
+                                key = key + (tuple(f.uv[j]), )
+                        if have_col:
+                                key = key + ((f.col[j].r, f.col[j].g, f.col[j].b, f.col[j].a), )
+                        print >> file, "%d" % vdict[key],
+                print >> file
+                if not i % 100 and meshtools.show_progress:
+                        Blender.Window.DrawProgressBar(float(i) / len(mesh.faces), "Writing faces")
+
+       Blender.Window.DrawProgressBar(1.0, '')  # clear progressbar
+       file.close()
+       message = "Successfully exported " + Blender.sys.basename(filename)
+       meshtools.print_boxed(message)
+
+Blender.Window.FileSelector(file_callback, "PLY Export")
diff --git a/release/scripts/ply_import.py b/release/scripts/ply_import.py
new file mode 100644 (file)
index 0000000..9df72ca
--- /dev/null
@@ -0,0 +1,292 @@
+#!BPY
+
+"""
+Name: 'PLY...'
+Blender: 237
+Group: 'Import'
+Tip: 'Import a Stanford PLY file'
+"""
+
+__author__ = 'Bruce Merry'
+__version__ = '0.92'
+__bpydoc__ = """\
+This script imports Stanford PLY files into Blender. It supports per-vertex
+normals, and per-face colours and texture coordinates.
+
+Usage:
+
+Run this script from "File->Import" and select the desired PLY file.
+"""
+
+# Copyright (C) 2004, 2005: Bruce Merry, bmerry@cs.uct.ac.za
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+
+# Updated by Campbell Barton AKA Ideasman, 10% faster code.
+
+# Portions of this code are taken from mod_meshtools.py in Blender
+# 2.32.
+
+import Blender, meshtools
+import re, struct, StringIO
+
+class element_spec:
+       name = ''
+       count = 0
+       def __init__(self, name, count):
+               self.name = name
+               self.count = count
+               self.properties = []
+
+       def load(self, format, stream):
+               if format == 'ascii':
+                       stream = re.split('\s+', stream.readline())
+               return map(lambda x: x.load(format, stream), self.properties)
+
+       def index(self, name):
+               for i, p in enumerate(self.properties):
+                       if p.name == name: return i
+               return -1
+
+class property_spec:
+       name = ''
+       list_type = ''
+       numeric_type = ''
+       def __init__(self, name, list_type, numeric_type):
+               self.name = name
+               self.list_type = list_type
+               self.numeric_type = numeric_type
+
+       def read_format(self, format, count, num_type, stream):
+               if format == 'ascii':
+                       if (num_type == 's'):
+                               ans = []
+                               for i in xrange(count):
+                                       s = stream[i]
+                                       if len(s) < 2 or s[0] != '"' or s[-1] != '"':
+                                               print 'Invalid string', s
+                                               print 'Note: ply_import.py does not handle whitespace in strings'
+                                               return None
+                                       ans.append(s[1:-1])
+                               stream[:count] = []
+                               return ans
+                       if (num_type == 'f' or num_type == 'd'):
+                               mapper = float
+                       else:
+                               mapper = int
+                       ans = map(lambda x: mapper(x), stream[:count])
+                       stream[:count] = []
+                       return ans
+               else:
+                       if (num_type == 's'):
+                               ans = []
+                               for i in xrange(count):
+                                       fmt = format + 'i'
+                                       data = stream.read(struct.calcsize(fmt))
+                                       length = struct.unpack(fmt, data)[0]
+                                       fmt = '%s%is' % (format, length)
+                                       data = stream.read(struct.calcsize(fmt))
+                                       s = struct.unpack(fmt, data)[0]
+                                       ans.append(s[:-1]) # strip the NULL
+                               return ans
+                       else:
+                               fmt = '%s%i%s' % (format, count, num_type)
+                               data = stream.read(struct.calcsize(fmt));
+                               return struct.unpack(fmt, data)
+
+       def load(self, format, stream):
+               if (self.list_type != None):
+                       count = int(self.read_format(format, 1, self.list_type, stream)[0])
+                       return self.read_format(format, count, self.numeric_type, stream)
+               else:
+                       return self.read_format(format, 1, self.numeric_type, stream)[0]
+
+class object_spec:
+       'A list of element_specs'
+       specs = []
+
+       def load(self, format, stream):
+               return dict([(i.name,[i.load(format, stream) for j in xrange(i.count) ]) for i in self.specs])
+               
+               '''
+               answer = {}
+               for i in self.specs:
+                       answer[i.name] = []
+                       for j in xrange(i.count):
+                               if not j % 100 and meshtools.show_progress:
+                                       Blender.Window.DrawProgressBar(float(j) / i.count, 'Loading ' + i.name)
+                               answer[i.name].append(i.load(format, stream))
+               return answer
+                       '''
+               
+
+def read(filename):
+       format = ''
+       version = '1.0'
+       format_specs = {'binary_little_endian': '<',
+                       'binary_big_endian': '>',
+                       'ascii': 'ascii'}
+       type_specs = {'char': 'b',
+                     'uchar': 'B',
+                     'int8': 'b',
+                     'uint8': 'B',
+                     'int16': 'h',
+                     'uint16': 'H',
+                     'int': 'i',
+                     'int32': 'i',
+                     'uint': 'I',
+                     'uint32': 'I',
+                     'float': 'f',
+                     'float32': 'f',
+                     'float64': 'd',
+                     'string': 's'}
+       obj_spec = object_spec()
+
+       try:
+               file = open(filename, 'rb')
+               signature = file.readline()
+               if (signature != 'ply\n'):
+                       print 'Signature line was invalid'
+                       return None
+               while 1:
+                       tokens = re.split(r'[ \n]+', file.readline())
+                       if (len(tokens) == 0):
+                               continue
+                       if (tokens[0] == 'end_header'):
+                               break
+                       elif (tokens[0] == 'comment' or tokens[0] == 'obj_info'):
+                               continue
+                       elif (tokens[0] == 'format'):
+                               if (len(tokens) < 3):
+                                       print 'Invalid format line'
+                                       return None
+                               if (tokens[1] not in format_specs.keys()):
+                                       print 'Unknown format', tokens[1]
+                                       return None
+                               if (tokens[2] != version):
+                                       print 'Unknown version', tokens[2]
+                                       return None
+                               format = tokens[1]
+                       elif (tokens[0] == 'element'):
+                               if (len(tokens) < 3):
+                                       print 'Invalid element line'
+                                       return None
+                               obj_spec.specs.append(element_spec(tokens[1], int(tokens[2])))
+                       elif (tokens[0] == 'property'):
+                               if (not len(obj_spec.specs)):
+                                       print 'Property without element'
+                                       return None
+                               if (tokens[1] == 'list'):
+                                       obj_spec.specs[-1].properties.append(property_spec(tokens[4], type_specs[tokens[2]], type_specs[tokens[3]]))
+                               else:
+                                       obj_spec.specs[-1].properties.append(property_spec(tokens[2], None, type_specs[tokens[1]]))
+               obj = obj_spec.load(format_specs[format], file)
+
+       except IOError, (errno, strerror):
+               file.close()
+               return None
+
+       file.close()
+       return (obj_spec, obj);
+
+def add_face(vertices, varr, indices, uvindices, colindices):
+       face = Blender.NMesh.Face([varr[i] for i in indices])
+       for index in indices:
+               vertex = vertices[index];
+               
+               if uvindices:
+                       face.uv.append((vertex[uvindices[0]], 1.0 - vertex[uvindices[1]]))
+               if colindices:
+                       if not uvindices: face.uv.append((0, 0)) # Force faceUV
+                       face.col.append(Blender.NMesh.Col(vertex[colindices[0]], vertex[colindices[1]], vertex[colindices[2]], 255))
+       return face
+
+def filesel_callback(filename):
+       t = Blender.sys.time()
+       (obj_spec, obj) = read(filename)
+       if obj == None:
+               print 'Invalid file'
+               return
+       vmap = {}
+       varr = []
+       uvindices = None
+       noindices = None
+       colindices = None
+       for el in obj_spec.specs:
+               if el.name == 'vertex':
+                       vindices = vindices_x, vindices_y, vindices_z = (el.index('x'), el.index('y'), el.index('z'))
+                       if el.index('nx') >= 0 and el.index('ny') >= 0 and el.index('nz') >= 0:
+                               noindices = (el.index('nx'), el.index('ny'), el.index('nz'))
+                       if el.index('s') >= 0 and el.index('t') >= 0:
+                               uvindices = (el.index('s'), el.index('t'))
+                       if el.index('red') >= 0 and el.index('green') and el.index('blue') >= 0:
+                               colindices = (el.index('red'), el.index('green'), el.index('blue'))
+               elif el.name == 'face':
+                       findex = el.index('vertex_indices')
+               
+
+       mesh = Blender.NMesh.GetRaw()
+       NMVert = Blender.NMesh.Vert
+       for v in obj['vertex']:
+               
+               if noindices > 0:
+                       x,y,z,nx,ny,nz = vkey =\
+                       (v[vindices_x], v[vindices_y], v[vindices_z],\
+                       v[noindices[0]], v[noindices[1]], v[noindices[2]])
+               else:
+                       x,y,z = vkey = (v[vindices_x], v[vindices_y], v[vindices_z])
+               #if not vmap.has_key(vkey):
+               try: # try uses 1 less dict lookup
+                       varr.append(vmap[vkey])
+               except:
+                       nmv = NMVert(vkey[0], vkey[1], vkey[2])
+                       mesh.verts.append(nmv)
+                       if noindices > 0:
+                               nmv.no[0] = vkey[3]
+                               nmv.no[1] = vkey[4]
+                               nmv.no[2] = vkey[5]
+                       vmap[vkey] = nmv
+                       varr.append(vmap[vkey])
+       
+       verts = obj['vertex']
+       for f in obj['face']:
+               ind = f[findex]
+               nind = len(ind)
+               if nind <= 4:
+                       mesh.faces.append(add_face(verts, varr, ind, uvindices, colindices))
+               else:
+                       for j in xrange(nind - 2):
+                               mesh.faces.append(add_face(verts, varr, (ind[0], ind[j + 1], ind[j + 2]), uvindices, colindices))
+
+       
+       del obj # Reclaim memory
+
+       if noindices:
+               normals = 1
+       else:
+               normals = 0
+       objname = Blender.sys.splitext(Blender.sys.basename(filename))[0]
+       if not meshtools.overwrite_mesh_name:
+               objname = meshtools.versioned_name(objname)
+       Blender.NMesh.PutRaw(mesh, objname, not normals)
+       Blender.Object.GetSelected()[0].name = objname
+       Blender.Redraw()
+       Blender.Window.DrawProgressBar(1.0, '')
+       message = 'Successfully imported ' + Blender.sys.basename(filename) + ' ' + str(Blender.sys.time()-t)
+       meshtools.print_boxed(message)
+
+Blender.Window.FileSelector(filesel_callback, 'Import PLY')
+
diff --git a/release/scripts/xfig_export.py b/release/scripts/xfig_export.py
new file mode 100644 (file)
index 0000000..9428412
--- /dev/null
@@ -0,0 +1,441 @@
+#!BPY
+
+"""
+Name: 'xfig export (.fig)'
+Blender: 237
+Group: 'Export'
+Tooltip: 'Export selected mesh to xfig Format (.fig)'
+"""
+
+__author__ = "Dino Ghilardi  "
+__url__ = ("blender", "elysiun")
+__version__ = "1.1"
+
+__bpydoc__ = """\
+               This script exports the selected mesh to xfig (www.xfig.org) file format (i.e.: .fig)
+
+               The starting point of this script was Anthony D'Agostino's raw triangle format export.
+               (some code is still here and there, cut'n pasted from his script)
+
+               Usage:<br>
+                       Select the mesh to be exported and run this script from "File->Export" menu.
+                       The toggle button 'export 3 files' enables the generation of 4 files: one global
+                       and three with the three different views of the object.
+                       This script is licensed under the GPL license. (c) Dino Ghilardi, 2005
+                       
+"""
+
+# .fig export, mostly brutally cut-n pasted from the 
+# 'Raw triangle export' (Anthony D'Agostino, http://www.redrival.com/scorpius)|
+
+import Blender
+from Blender import NMesh, Draw, BGL
+from Blender.Window import DrawProgressBar
+#, meshtools
+import sys
+#import time
+
+# =================================
+# === Write xfig Format.===
+# =================================
+
+#globals definition and init
+mystring = ""
+mymsg = ""
+toggle=0
+sel3files=0
+maxX=-1000000000
+maxY=-1000000000
+maxZ=-1000000000
+minX=minY=minZ=10000000000
+boolmode=0 #0= export in inches, 1= export in cm
+dimscale=float(1200) #scale due to the cm/inches select. default: inches
+hidden_flag=0
+
+space = float(2) #space between figures, in blender units.
+scale= float(1200) #conversion scale to xfig units.
+guiscale=float(1) #scale shown on the ruler in the GUI
+#return values from gui items, just to deallocate them on exit.
+guiret1=guiret2=guiret3=guiret4=guiret5=guiret6=guiret7=0
+
+ScalePopup=0
+DistancePopup=0
+SpacePopup=0
+#end of globals definition
+
+
+def getmaxmin():
+       """Gets the max-min coordinates of the mesh"""
+       
+       global maxX,maxY,maxZ,minX,minY,minZ
+       """Getting the extremes of the mesh to be exported"""
+       objects = Blender.Object.GetSelected()
+       objname = objects[0].name
+       meshname = objects[0].data.name
+       mesh = Blender.NMesh.GetRaw(meshname)
+       obj = Blender.Object.Get(objname)
+       #initializing max-min find.
+       # ...is there a standard python function to find those values?
+       face =mesh.faces[1]
+       if len(face.v)==3:
+               v1,v2,v3=face.v
+       if len(face.v)==4:
+               v1,v2,v3,v4=face.v
+       if len(face.v)==2: 
+               v1,v2=face.v
+       #is the next condition a nonsense for  a face? ...Anyway, to be sure....
+       if len(face.v)==1:
+               v1=face.v
+       
+       maxX,maxY,maxZ  = v1
+       minX, minY, minZ = v1
+       
+       for face in mesh.faces:
+           #if (face.flag & Blender.NMesh.FaceFlags['ACTIVE']):        
+                               if len(face.v) == 3:            # triangle
+                                       v1, v2, v3 = face.v
+                                       x1,y1,z1 = v1.co
+                                       x2,y2,z2 = v2.co
+                                       x3,y3,z3 = v3.co
+                                       maxX = max (maxX, x1, x2, x3)
+                                       maxY = max (maxY, y1, y2, y3)
+                                       maxZ = max (maxZ, z1, z2, z3)
+                                       minX = min (minX, x1, x2, x3)
+                                       minY = min (minY, y1, y2, y3)
+                                       minZ = min (minZ, z1, z2, z3)
+                               elif len(face.v)==2:
+                                       v1,v2=face.v
+                                       x1,y1,z1 = v1.co
+                                       x2,y2,z2 = v2.co
+                                       maxX = max (maxX, x1, x2)
+                                       maxY = max (maxY, y1, y2)
+                                       maxZ = max (maxZ, z1, z2)
+                                       minX = min (minX, x1, x2)
+                                       minY = min (minY, y1, y2)
+                                       minZ = min (minZ, z1, z2)
+                               elif len(face.v)==1:
+                                       v1=face.v
+                                       x1,y1,z1 = v1.co
+                                       maxX = max (maxX, x1)
+                                       maxY = max (maxY, y1)
+                                       maxZ = max (maxZ, z1)
+                                       minX = min (minX, x1)
+                                       minY = min (minY, y1)
+                                       minZ = min (minZ, z1)
+                               elif len(face.v)==4: 
+                                       v1,v2,v3,v4=face.v
+                                       x1,y1,z1 = v1.co
+                                       x2,y2,z2 = v2.co
+                                       x3,y3,z3 = v3.co
+                                       x4,y4,z4 = v4.co
+                                       maxX = max (maxX, x1, x2, x3, x4)
+                                       maxY = max (maxY, y1, y2, y3, y4)
+                                       maxZ = max (maxZ, z1, z2, z3, z4)
+                                       minX = min (minX, x1, x2, x3, x4)
+                                       minY = min (minY, y1, y2, y3, y4)
+                                       minZ = min (minZ, z1, z2, z3, z4)
+def xfigheader():
+       global export_type
+       print "#FIG 3.2  Produced by xfig version 3.2.5-alpha5"
+       print "Landscape"
+       print"Center"
+       if boolmode==0:
+               print"Inches"
+       else:
+               print"Metric"
+       #print export_type
+       print"Letter"
+       print"100.00"
+       print"Single"
+       print "-2"
+       print "1200 2"
+
+def xytransform (face):
+       """gives the face vertexes coordinates in the xfig format/translation (view xy)"""
+       v4=None
+       x4=y4=z4=None
+       if len(face.v)==3:
+         v1,v2,v3=face.v
+       else:
+         v1,v2,v3,v4=face.v    
+       x1,y1,z1 = v1.co
+       x2,y2,z2 = v2.co
+       x3,y3,z3 = v3.co
+       y1=-y1
+       y2=-y2
+       y3=-y3
+       if v4 !=None:
+               x4,y4,z4 = v4.co
+               y4=-y4
+       return x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4 
+
+def xztransform(face):
+       """gives the face vertexes coordinates in the xfig format/translation (view xz)"""
+       v4=None
+       x4=y4=z4=None
+       if len(face.v)==3:
+         v1,v2,v3=face.v
+       else:
+         v1,v2,v3,v4=face.v    
+
+       #Order vertexes
+       x1,y1,z1 = v1.co
+       x2,y2,z2 = v2.co
+       x3,y3,z3 = v3.co
+       y1=-y1
+       y2=-y2
+       y3=-y3
+       
+       z1=-z1+maxZ-minY +space
+       z2=-z2+maxZ-minY +space
+       z3=-z3+maxZ-minY +space
+
+       if v4 !=None:
+               x4,y4,z4 = v4.co
+               y4=-y4
+               z4=-z4+maxZ-minY +space 
+       return x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4 
+
+def yztransform(face):
+       """gives the face vertexes coordinates in the xfig format/translation (view xz)"""
+       v4=None
+       x4=y4=z4=None
+       if len(face.v)==3:
+         v1,v2,v3=face.v
+       else:
+         v1,v2,v3,v4=face.v    
+
+       #Order vertexes
+       x1,y1,z1 = v1.co
+       x2,y2,z2 = v2.co
+       x3,y3,z3 = v3.co
+       y1=-y1
+       y2=-y2
+       y3=-y3
+       z1=-(z1-maxZ-maxX-space)
+       z2=-(z2-maxZ-maxX-space)
+       z3=-(z3-maxZ-maxX-space)
+
+       if v4 !=None:
+               x4,y4,z4 = v4.co
+               y4=-y4
+               z4=-(z4-maxZ-maxX-space)
+       return x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4 
+
+def figdata(expview):
+       """Prints all the xfig data (no header)"""
+       objects = Blender.Object.GetSelected()
+       objname = objects[0].name
+       meshname = objects[0].data.name
+       mesh = Blender.NMesh.GetRaw(meshname)
+       obj = Blender.Object.Get(objname)
+       facenumber = len(mesh.faces)
+       for face in mesh.faces:
+                       if len(face.v) == 3:            # triangle
+                               print "2 3 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4"
+                               if expview=="xy":
+                                       x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4=xytransform(face)
+                                       faceverts = int(x1*scale),int(y1*scale),int(x2*scale),int(y2*scale),int(x3*scale),int(y3*scale), int(x1*scale),int(y1*scale)
+                               elif expview=="xz":
+                                       x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4=xztransform(face)
+                                       faceverts = int(x1*scale),int(z1*scale),int(x2*scale),int(z2*scale),int(x3*scale),int(z3*scale), int(x1*scale),int(z1*scale)
+                               elif expview=="yz":
+                                       x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4=yztransform(face)
+                                       faceverts = int(z1*scale),int(y1*scale),int(z2*scale),int(y2*scale),int(z3*scale),int(y3*scale),int(z1*scale),int(y1*scale)
+                               print "\t% i % i % i % i % i % i % i % i" % faceverts
+                       else:
+                               if len(face.v) == 4: #quadrilateral     
+                                       print "2 3 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5"
+                                       if expview=="xy":
+                                         x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4=xytransform(face)
+                                         faceverts = int(x1*scale),int(y1*scale),int(x2*scale),int(y2*scale),int(x3*scale),int(y3*scale),int(x4*scale),int(y4*scale), int(x1*scale),int(y1*scale)
+
+                                       elif expview=="xz":
+                                         x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4=xztransform(face)
+                                         faceverts = int(x1*scale),int(z1*scale),int(x2*scale),int(z2*scale),int(x3*scale),int(z3*scale),int(x4*scale),int(z4*scale), int(x1*scale),int(z1*scale)
+
+                                       elif expview=="yz":
+                                         x1,y1,z1,x2,y2,z2,x3,y3,z3,x4,y4,z4=yztransform(face)
+                                         faceverts = int(z1*scale),int(y1*scale),int(z2*scale),int(y2*scale),int(z3*scale),int(y3*scale),int(z4*scale),int(y4*scale), int(z1*scale),int(y1*scale)
+                                       print "\t% i % i % i % i % i % i % i % i % i % i" % faceverts
+                               else: 
+                                       pass #it should not occour, but....
+
+
+def writexy(filename):
+       """writes the x-y view file exported"""
+       global maxX, maxY, maxZ
+       global minX, minY, minZ
+       global space
+       global scale
+       #start = time.clock()
+       file = open(filename, "wb")
+       std=sys.stdout
+       sys.stdout=file
+       xfigheader()
+       figdata("xy")# xydata()
+       sys.stdout=std
+       Blender.Window.DrawProgressBar(1.0, '')  # clear progressbar
+       file.close()
+       #end = time.clock()
+       #seconds = " in %.2f %s" % (end-start, "seconds")
+       message = "Successfully exported " + Blender.sys.basename(filename)# + seconds
+       print message 
+
+def writexz(filename):
+       """writes the x-z view file exported"""
+       global space,maxX,maxY,maxZ, scale
+       #start = time.clock()
+       file = open(filename, "wb")
+       std=sys.stdout
+       sys.stdout=file
+       xfigheader()
+       figdata("xz")#xzdata()
+       sys.stdout=std
+       Blender.Window.DrawProgressBar(1.0, '')  # clear progressbar
+       file.close()
+       #end = time.clock()
+       #seconds = " in %.2f %s" % (end-start, "seconds")
+       message = "Successfully exported " + Blender.sys.basename(filename)# + seconds
+       print message 
+
+def writeyz(filename):
+       """writes the y-z view file exported"""
+       global maxX, maxY, maxZ, minX, minY, minZ,scale
+       #start = time.clock()
+       file = open(filename, "wb")
+
+       std=sys.stdout
+       sys.stdout=file
+
+       xfigheader()
+       figdata("yz")#yzdata()  
+       sys.stdout=std
+       Blender.Window.DrawProgressBar(1.0, '')  # clear progressbar
+       file.close()
+       #end = time.clock()
+       #seconds = " in %.2f %s" % (end-start, "seconds")
+       message = "Successfully exported " + Blender.sys.basename(filename)# + seconds
+       print message 
+
+def writeall(filename):
+       """writes all 3 views
+       
+       Every view is a combined object in the resulting xfig. file."""
+       global maxX, maxY, maxZ, minX, minY, minZ,scale
+       #start = time.clock()
+       file = open(filename, "wb")
+
+       std=sys.stdout
+       sys.stdout=file
+
+       xfigheader()
+       print "#upper view (7)"
+       print "6 % i % i % i % i ",  minX, minY, maxX, maxY
+       figdata("xy") #xydata()
+       print "-6"
+       print "#bottom view (1)"
+       print "6 %i %i %i %i", minX, -minZ+maxZ-minY +space, maxX,-maxZ+maxZ-minY +space
+       figdata ("xz") #xzdata()
+       print "-6"
+       
+       print "#right view (3)"
+       print "6 %i %i %i %i", minX, minZ-maxZ-maxX-space, maxX,maxZ-maxZ-maxX-space
+       figdata ("yz") #yzdata()        
+       print "-6"
+       
+       sys.stdout=std
+       Blender.Window.DrawProgressBar(1.0, '')  # clear progressbar
+       file.close()
+       #end = time.clock()
+       #seconds = " in %.2f %s" % (end-start, "seconds")
+       message = "Successfully exported " + Blender.sys.basename(filename)# + seconds
+
+
+#********************************************************USER INTERFACE*****************************************************
+#********************************************************USER INTERFACE*****************************************************
+#********************************************************USER INTERFACE*****************************************************
+def gui():              # the function to draw the screen
+  global mystring, mymsg, toggle, sel3files, scale 
+  global guiret1, guiret2, guiret3, guiret4, guiret5, guiret6, guiret7
+  global ScalePopup, SpacePopup, boolmode, guiscale,hidden_flag
+  if len(mystring) > 90: mystring = ""
+  BGL.glClearColor(0,0,1,1)
+  BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)
+  BGL.glColor3f(1,1,1)
+  guiret2=Draw.PushButton("Cancel", 2, 10, 10, 55, 20,"Cancel")
+  guiret3=Draw.Toggle("1 file per view",    3, 10, 40, 110,20, sel3files, "3 files")
+  guiret4=Draw.PushButton("Export", 4, 70, 10, 70, 20, "Select filename and export") 
+
+  ScalePopup=Draw.Number("Scale", 5, 10,70, 110,20, guiscale, 0.0001, 1000.1, "Scaling factor")
+  SpacePopup=Draw.Number("Space", 6, 10,90, 110,20, space, 0, 10000, "Space between projections")
+  
+  guiret5=Draw.Toggle("cm", 7, 120,70, 40,20, boolmode, "set scale to 1 blender unit = 1 cm in xfig")
+  guiret6=Draw.Toggle("in", 8, 162,70, 40,20, not boolmode, "set scale to 1 blender unit = 1 in in xfig")
+#  guiret7 = guiret6=Draw.Toggle("only visible", 9, 120,90, 82,20, hidden_flag, "hidden faces")
+
+
+  BGL.glRasterPos2i(72, 16)
+  if toggle: toggle_state = "down"
+  else: toggle_state = "up"
+  #Draw.Text("The toggle button is %s." % toggle_state, "small")
+  BGL.glRasterPos2i(10, 230)
+  #Draw.Text("Type letters from a to z, ESC to leave.")
+  BGL.glRasterPos2i(20, 200)
+  Draw.Text(mystring)
+  BGL.glColor3f(1,0.4,0.3)
+  BGL.glRasterPos2i(340, 70)
+  Draw.Text(mymsg, "tiny")
+
+
+def event(evt, val):    # the function to handle input events
+  Draw.Redraw(1)
+
+def button_event(evt):  # the function to handle Draw Button events
+       global toggle, guiret5,scale, space, SpacePopup, boolmode, dimscale, guiscale
+       global hidden_flag, sel3files
+       if evt==1:
+         toggle = 1 - toggle
+         Draw.Redraw(1)
+       if evt==2:
+         Draw.Exit()
+         return
+       if evt==3:
+         sel3files = 1-sel3files
+         Draw.Redraw(1)        
+       if evt==4:      
+         Blender.Window.FileSelector(fs_callback, "Export fig")
+         Draw.Exit()
+         return 
+       if evt==5:
+         guiscale = ScalePopup.val
+         scale=dimscale*guiscale
+         Draw.Redraw(1)
+       if evt==6:
+         space =SpacePopup.val
+       if evt==7: 
+               boolmode=1
+               dimscale=450 #converting to cm
+               scale = dimscale*guiscale
+               Draw.Redraw(1)
+       if evt==8:
+               boolmode=0
+               dimscale = 1200
+               scale = dimscale*guiscale
+               Draw.Redraw(1)
+       if evt==9:
+               hidden_flag=1-hidden_flag
+               Draw.Redraw(1)
+
+Draw.Register(gui, event, button_event)  # registering the 3 callbacks
+       
+def fs_callback(filename):
+       if filename.find('.fig', -4) > 0: filename = filename[:-4]
+       getmaxmin()
+       if sel3files:
+               writexy(filename+"_XY.fig")
+               writexz(filename+"_XZ.fig")
+               writeyz(filename+"_YZ.fig")
+       writeall(filename+"_ALL.fig")
+       print  scale
+       Draw.Exit()
diff --git a/source/blender/python/api2_2x/doc/Pose.py b/source/blender/python/api2_2x/doc/Pose.py
new file mode 100644 (file)
index 0000000..c35807e
--- /dev/null
@@ -0,0 +1,104 @@
+# Blender.Object.Pose module
+
+"""
+The Blender.Object.Pose submodule.
+
+Pose
+====
+
+This module provides access to B{Pose} objects in Blender.  These Pose is the 
+current object-level (as opposed to armature-data level) transformation.
+
+Example::
+
+
+@var ROT: 
+@type ROT: Constant
+@var LOC: 
+@type LOC: Constant
+@var SIZE: 
+@type SIZE: Constant
+"""
+
+class Pose:
+  """
+  The Pose object
+  ===============
+    This object gives access to Pose-specific data in Blender.
+  @ivar bones: A Dictionary of PosePoseBones (PoseDict) that make up this Pose.
+  @type bones: PoseDict Object
+  """
+
+  def update():
+    """
+    Save all changes and update the Pose.
+    @rtype: None
+    """
+
+class PoseBonesDict:
+  """
+  The PoseBonesDict object
+  ========================
+    This object gives gives dictionary like access to the PoseBones in a Pose. 
+    It is internal to blender but is called as 'Pose.bones'
+  """
+
+  def items():
+    """
+    Retun the key, value pairs in this dictionary
+    @rtype: string, PosePoseBone
+    @return: All strings, and PosePoseBones in the Pose (in that order)
+    """
+
+  def keys():
+    """
+    Retun the keys in this dictionary
+    @rtype: string
+    @return: All strings representing the PosePoseBone names
+    """
+
+  def values():
+    """
+    Retun the values in this dictionary
+    @rtype: BPy_PoseBone
+    @return: All PosePoseBones in this dictionary
+    """
+
+class PoseBone:
+  """
+  The PoseBone object
+  ===================
+    This object gives access to PoseBone-specific data in Blender. 
+  @ivar name: The name of this PoseBone.
+  @type name: String
+  @ivar loc: The change in location for this PoseBone.
+  @type loc: Vector object
+  @ivar size: The change in size for this PoseBone (no chane is 1,1,1)
+  @type size: Vector object
+  @ivar quat: The change in rotation for this PoseBone.
+  @type quat: Quaternion object
+  @ivar head: The final head location for this PoseBone. (not settable)
+  @type head: Vector object
+  @ivar tail: The final tail location for this PoseBone. (not settable)
+  @type tail: Vector object
+  @ivar localMatrix: The matrix combination of rot/quat/loc.
+  @type localMatrix: Matrix object
+  @ivar poseMatrix: The total transformation of this PoseBone including constraints. (not settable)
+  @type poseMatrix: Matrix object
+  """
+
+  def insertKey(parentObject, frameNumber, type):
+    """
+    Insert a pose key for this PoseBone at a frame.
+    @type parentObject: Object object
+    @param parentObject: The object the pose came from.
+    @type frameNumber: integer
+    @param frameNumber: The frame number to insert the pose key on.
+    @type type: Constant object
+    @param type: Can be any combination of 3 Module constants:
+       - Pose.LOC
+       - Pose.ROT
+       - Pose.QUAT
+    @rtype: None
+    """
+