import bpy, mathutils, math
from mathutils import geometry

# Get a matrix for the selected faces that you can use to do local transforms
def get_selection_matrix(faces=False):
	
	me = bpy.context.active_object.data
	
	if not faces:
		faces = get_selected_faces()
	
	yVec = mathutils.Vector()
	zVec = mathutils.Vector()
	
	# Ok so we have a basic matrix, but lets base it more on the mesh!
	for f in faces:
			
		v1 = me.vertices[f.vertices[0]].co
		v2 = me.vertices[f.vertices[1]].co
		edge = v2-v1
		
		yVec += edge
		
		if len(f.vertices) == 4:
			v1 = me.vertices[f.vertices[2]].co
			v2 = me.vertices[f.vertices[3]].co
			edge = v1-v2
			
			yVec += edge
		
		zVec += mathutils.Vector(f.normal)
					
	if not yVec.length:
		quat = zVec.to_track_quat('-Z', 'Y')
		tMat = quat.to_matrix()
		yVec = tMat[1]
		yVec = yVec.normalized()
	else:
		yVec = yVec.normalized()
	zVec = zVec.normalized()
	
	# Rotate yVec so it's 90 degrees to zVec
	cross =yVec.cross(zVec)
	vec = float(yVec.angle(zVec) - math.radians(90))
	mat = mathutils.Matrix.Rotation(vec, 3, cross)
	yVec =  (mat * yVec)
	
	xVec = yVec.cross(zVec)
	
	xVec = xVec.normalized()
	
	nMat = mathutils.Matrix((xVec, yVec, zVec))
	
	return nMat



# Get the selection radius (minimum distance of an outer edge to the centre)
def get_selection_radius():

	ob = bpy.context.active_object

	radius = 0.0
	
	# no use continueing if nothing is selected
	if contains_selected_item(ob.data.polygons):
	
		# Find the center of the selection
		cent = mathutils.Vector()
		nr = 0
		nonVerts = []
		selVerts = []
		for f in ob.data.polygons:
			if f.select:
				nr += 1
				cent += f.center
			else:
				nonVerts.extend(f.vertices)
				
		cent /= nr
		
		chk = 0
		
		# Now that we know the center.. we can figure out how close the nearest point on an outer edge is
		for e in get_selected_edges():
		
			nonSection = [v for v in e.vertices if v in nonVerts]
			if len(nonSection):
			
				v0 = ob.data.vertices[e.vertices[0]].co
				v1 = ob.data.vertices[e.vertices[1]].co
				
				# If there's more than 1 vert of this edge on the outside... we need the edge length to be long enough too!
				if len(nonSection) > 1:
					edge = v0 - v1
					edgeRad = edge.length * 0.5
					
					if edgeRad < radius or not chk:
						radius = edgeRad
						chk += 1
				
				int = geometry.intersect_point_line(cent, v0, v1)
				
				rad = cent - int[0]
				l = rad.length
				
				if l < radius or not chk:
					radius = l
					chk += 1
					
	return radius
	
	
	
# Get the average length of the outer edges of the current selection
def get_shortest_outer_edge_length():

	ob = bpy.context.active_object

	min = False
	me = ob.data
	
	delVerts = []
	for f in me.faces:
		if not f.select:
			delVerts.extend(f.vertices)
	selEdges = [e.vertices for e in me.edges if e.select]

	if len(selEdges) and len(delVerts):
		
		for eVerts in selEdges:
			
			v0 = eVerts[0]
			v1 = eVerts[1]
			
			if v0 in delVerts and v1 in delVerts:
				ln = (me.vertices[v0].co - me.vertices[v1].co).length
				if min is False or (ln > 0.0 and ln < min):
					min = ln
						
	return min


# Get the average length of the outer edges of the current selection
def get_average_outer_edge_length():

	ob = bpy.context.active_object

	ave = 0.0
	me = ob.data
	
	delFaces = [f.vertices for f  in me.faces if not f.select]
	selEdges = [e.vertices for e in me.edges if e.select]

	if len(selEdges) and len(delFaces):
	
		number = 0
		
		for eVerts in selEdges:
			
			v0 = eVerts[0]
			v1 = eVerts[1]
			
			for fVerts in delFaces:
				if v0 in fVerts and v1 in fVerts:
					number += 1
					ave += (me.vertices[v0].co - me.vertices[v1].co).length
					break
						
		if number:
			ave /= number
			
	return ave


	
# Get the selected (or deselected items)
def get_selected(type='vertices',invert=False):
	
	mesh = bpy.context.active_object.data
	
	if type == 'vertices':
		items = mesh.vertices
	elif type == 'edges':
		items = mesh.edges
	else:
		items = mesh.polygons
		
	if invert:
		L = [i for i in items if not i.select]
	else:
		L = [i for i in items if i.select]
	return L
	
	
	
# See if the mesh has something selected
def has_selected(type='vertices',invert=False):
	
	mesh = bpy.context.active_object.data
	
	if type == 'vertices':
		items = mesh.vertices
	elif type == 'edges':
		items = mesh.edges
	else:
		items = mesh.polygons
		
	for i in items:
		if not invert and i.select:
			return True
		elif invert and not i.select:
			return True
			
	return False
		
		

# Get all the selected vertices (mode is selected or deselected)
def get_selected_vertices(mode='selected'):

	vertices = bpy.context.active_object.data.vertices

	if mode == 'deselected':
		L = [v for v in vertices if not v.select]
	else:
		L = [v for v in vertices if v.select]
	return L
	
	
	
# Get all the selected edges (mode is selected or deselected)
def get_selected_edges(mode='selected'):

	edges = bpy.context.active_object.data.edges

	if mode == 'deselected':
		L = [e for e in edges if not e.select]
	else:
		L = [e for e in edges if e.select]
	return L


	
# Get all the selected faces (mode is selected or deselected)
def get_selected_faces(mode='selected'):
	
	polygons = bpy.context.active_object.data.polygons
	
	if mode == 'deselected':
		L = [f for f in polygons if not f.select]
	else:
		L = [f for f in polygons if f.select]
	return L
	
	
	
# See if there is at least one selected item in 'items'
def contains_selected_item(items):

	for item in items:
		if item.select:
			return True
				
	return False