# 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()