#!BPY

"""
Name: 'Face GUI'
Blender: 234
Group: 'UV'
Tooltip: 'DEL'
"""

from Blender import *
from Blender.Mathutils import DotVecs, Vector
from math import sqrt

# button defaults
limit_btn = Draw.Create(0.01)


# Generic Math
def compare(f1, f2, limit):
  if f1 + limit > f2 and f1 - limit < f2:
    return 1
  return 0

def compare3(v1, v2, limit):
  if v1[0] + limit > v2[0] and v1[0] - limit < v2[0]:
      if v1[1] + limit > v2[1] and v1[1] - limit < v2[1]:
        if v1[2] + limit > v2[2] and v1[2] - limit < v2[2]:
          return 1
  return 0

def measure(v1, v2):
  return Mathutils.Vector([v1[0]-v2[0], v1[1] - v2[1], v1[2] - v2[2]]).length

def triArea2D(v1, v2, v3):
  e1 = measure(v1, v2)  
  e2 = measure(v2, v3)  
  e3 = measure(v3, v1)  
  p = e1+e2+e3
  return 0.25 * sqrt(abs(p*(p-2*e1)*(p-2*e2)*(p-2*e3)))

def faceArea(f):
  if len(f.v) == 4:
    return triArea2D(f.v[0].co, f.v[1].co, f.v[2].co) + triArea2D(f.v[0].co, f.v[2].co, f.v[3].co)
  elif len(f.v) == 3:
    return triArea2D(f.v[0].co, f.v[1].co, f.v[2].co)

def faceCent(f):
  x = y = z = 0
  for v in f.v:
    x += v.co[0]
    y += v.co[1]
    z += v.co[2]
  x = x/len(f.v)
  y = y/len(f.v)
  z = z/len(f.v)
  return Vector([x, y, z])

def getEdgeLengths(f):
  if len(f.v) == 4:
    return (measure(f.v[0].co, f.v[1].co), measure(f.v[1].co, f.v[2].co), measure(f.v[2].co, f.v[3].co) , measure(f.v[3].co, f.v[0].co) )
  elif len(f.v) == 3:
    return (measure(f.v[0].co, f.v[1].co), measure(f.v[1].co, f.v[2].co), measure(f.v[2].co, f.v[0].co) )






#========================================#
# Should we bother computing this faces  #
#========================================#
def fShouldCompare(f):
  # Only calculate for faces that will be affected.
  if len(f.v) < 3: # cant be an edge
    return 0
  elif f.flag == 64: # Ignore hidden
    return 0
  return 1

#====================================#
# Sanity checks                      #
#====================================#
def error(str):
	Draw.PupMenu('ERROR%t|'+str)

def getActMeshAndFace():
  selection = Object.GetSelected()
  if len(selection) == 0:
    error('No object selected')
    return None, None
  
  object = Object.GetSelected()[0]
  if object.getType() != 'Mesh':
    error('Not a mesh')
    return None, None
  mesh = object.getData()
  
  # We have a mesh so find AF.
  try:
    af = mesh.faces[mesh.getActiveFace()]
  except:
    error('no active face')
    return None, None
  
  # Okay everything seems sane
  return mesh, af
    
EVT_IDX = 1
SET_MAT = EVT_IDX; EVT_IDX+=1
def setMat(mesh):
  if len(Material.Get()) == 0:
    error('No Materials in this scene')
  else:
    menu = 'Set Material%t|'
    for mat in Material.Get():
      menu += mat.name + '|'
    
    matIdx = Draw.PupMenu(menu)-1
    if matIdx != -1:
      try:
        mesh.addMaterial(Material.Get()[matIdx])
        mesh.update()
      except:
        pass
        
      mIdx = 0
      meshMats = mesh.getMaterials()
      while mIdx < len(meshMats):
        if meshMats[mIdx].name == Material.Get()[matIdx].name:
          localMatIdx = mIdx
        mIdx +=1
      
      for f in mesh.getSelectedFaces(0):
        f.mat = localMatIdx
      mesh.update()



DEL_SEL_FACES = EVT_IDX; EVT_IDX+=1
def delSelFaces(mesh):
  fIdx= 0
  while fIdx < len(mesh.faces):
    if mesh.faces[fIdx].flag & NMesh.FaceFlags['SELECT']:
      mesh.faces.remove(mesh.faces[fIdx])
      fIdx-=1
    fIdx+=1
    
FLIP_SEL_FACES = EVT_IDX; EVT_IDX+=1
def flipSelFaces(mesh):
  for f in mesh.getSelectedFaces(0):
    if fShouldCompare(f):
        f.v.reverse()
        f.uv.reverse()
        f.col.reverse()


FLIP_UV_X_FACES = EVT_IDX; EVT_IDX+=1
def flipUvXFaces(mesh):
  for f in mesh.getSelectedFaces(0):
    if fShouldCompare(f):
        f.v.reverse()
        f.uv.reverse()
        f.col.reverse()


FLIP_UV_Y_FACES = EVT_IDX; EVT_IDX+=1
def flipUvYFaces(mesh):
  for f in mesh.getSelectedFaces(0):
    if fShouldCompare(f):
        f.v.reverse()
        f.uv.reverse()
        f.col.reverse()


COPY_ACT_MAT = EVT_IDX; EVT_IDX+=1
def copyActMat(mesh, af):
  mat = af.mat
  for f in mesh.getSelectedFaces():
    f.mat = mat

COPY_ACT_IMG = EVT_IDX; EVT_IDX+=1
def copyActImg(mesh, af):
  img = af.image
  if img != None:
    for f in mesh.getSelectedFaces():
      f.mode |= NMesh.FaceModes['TEX']
      f.image = img
    
    

SEL_LINKED_IMAGE = EVT_IDX; EVT_IDX+=1
SEL_LINKED_MAT = EVT_IDX; EVT_IDX+=1
SEL_LINKED_NORMAL = EVT_IDX; EVT_IDX+=1
SEL_LINKED_AREA = EVT_IDX; EVT_IDX+=1
def selectLinkedMulti(mesh, af, mode):
  limit = limit_btn.val
  faceList = []
  if mode == 'image':
    active_img = af.image
    for f in mesh.faces:
      if len(f.v) > 1:
        if active_img == f.image: # compare.
          faceList.append(f)
  if mode == 'mat':
    mat = af.mat
    for f in mesh.faces:
      if len(f.v) > 1:
        if mat == f.mat: # compare.
          faceList.append(f)          
  elif mode == 'normal':
    for f in mesh.faces:
      if fShouldCompare(f):
        if compare3(af.no, f.no, limit):
          faceList.append(f)
  elif mode == 'area':
    afArea = faceArea(af)
    for f in mesh.faces:
      if fShouldCompare(f):
        if compare(afArea, faceArea(f), limit):
          faceList.append(f)
        
        
  # Takes a list of faces and find the ones that are adjecent to the face.
  def selectAdjacentFaces(face): # faceList is also needed, local atm.
    myFaceList= []
    for f in faceList:
      if not f.flag & NMesh.FaceFlags['SELECT']: # dont bother with selected faces.
        for v in f.v:
          if v in face.v:
            f.flag |= NMesh.FaceFlags['SELECT']
            myFaceList.append(f)
            break
    return myFaceList
  
  adjFacesTested = []
  
  def selLinked(f):
    adjFaces= selectAdjacentFaces(f)
    for ff in adjFaces:
      if ff not in adjFacesTested:
        adjFacesTested.append(ff)
        selLinked(ff)
      
  selLinked(af)



SEL_SAME_IMAGE = EVT_IDX; EVT_IDX+=1
def selSameImage(mesh, af):
  active_img =  af.image
  for f in mesh.faces:
    if fShouldCompare(f):
      if active_img == f.image:
        f.flag |= NMesh.FaceFlags['SELECT']



SEL_SAME_AREA = EVT_IDX; EVT_IDX+=1
def selSameArea(mesh, af):
  limit = limit_btn.val
  afArea = faceArea(af)
  limit = limit_btn.val * afArea # Make the lomot proportinal to the 
  for f in mesh.faces:
    if fShouldCompare(f):
      if compare(afArea, faceArea(f), limit):
        f.flag |= NMesh.FaceFlags['SELECT']

SEL_SAME_MAT = EVT_IDX; EVT_IDX+=1
def selSameMat(mesh, af):
  mat =  af.mat
  for f in mesh.faces:
    if mat == f.mat:
      f.flag |= NMesh.FaceFlags['SELECT']

SEL_SAME_NORMAL = EVT_IDX; EVT_IDX+=1
def selSameNormal(mesh, af):
  limit = limit_btn.val
  for f in mesh.faces:
    if fShouldCompare(f):
      if compare3(af.no, f.no, limit):
        f.flag |= NMesh.FaceFlags['SELECT']

SEL_SAME_COPLANER = EVT_IDX; EVT_IDX+=1
def selSameCoplaner(mesh, af):
  limit = limit_btn.val
  nlimit = limit * 2 # * 1 # limit for normal test
  climit = limit * 3 # limit for coplaner test.
  afCent = faceCent(af)
  for f in mesh.faces:
    if fShouldCompare(f):
      match = 0
      if compare3(af.no, f.no, nlimit):
        fCent = faceCent(f)
        if abs(DotVecs(Vector([af.no[0], af.no[1], af.no[2]]), afCent ) - DotVecs(Vector([af.no[0], af.no[1], af.no[2]]), fCent )) <= climit:
          match = 1
      if match:
        f.flag |= NMesh.FaceFlags['SELECT']

def draw_gui():
  global limit_btn
  # find the width of the widest button
  fontsize = "small"
  button_width = 0
  button_height = 15
  button_width = 60
  ROW = 0
  COLUM = 0
  txtBuffer = 2
  
  limit_btn = Draw.Number('', 0, button_width*COLUM, button_height*ROW,  button_width, button_height, limit_btn.val, 0.0, 1.0, 'limit'); ROW+=1
  Draw.PushButton('Flip', FLIP_SEL_FACES, button_width*COLUM, button_height*ROW, button_width, button_height, 'tooltp'); ROW+=1
  Draw.PushButton('Del',    DEL_SEL_FACES,  button_width*COLUM, button_height*ROW, button_width, button_height, 'tooltp'); ROW+=1  
  Draw.PushButton('Set Mat',    SET_MAT,  button_width*COLUM, button_height*ROW, button_width, button_height, 'tooltp'); ROW+=1  
  BGL.glRasterPos2i(txtBuffer + button_width*COLUM, txtBuffer + button_height*ROW)
  Draw.Text("Face", fontsize)	
  
  
  
  ROW+=1
  '''
  ROW = 0
  COLUM+=1
  '''
  Draw.PushButton('Mat', COPY_ACT_MAT, button_width*COLUM, button_height*ROW,  button_width, button_height, 'tooltp'); ROW+=1
  Draw.PushButton('Image', COPY_ACT_IMG, button_width*COLUM, button_height*ROW, button_width, button_height, 'tooltp'); ROW+=1
  BGL.glRasterPos2i(txtBuffer + button_width*COLUM, txtBuffer + button_height*ROW)
  Draw.Text("Copy", fontsize)	  
  
  '''
  ROW+=1
  '''
  
  ROW = 0
  COLUM+=1
  
  Draw.PushButton('Image', SEL_LINKED_IMAGE, button_width*COLUM, button_height*ROW,  button_width, button_height, 'tooltp'); ROW+=1
  Draw.PushButton('Mat', SEL_LINKED_MAT, button_width*COLUM, button_height*ROW,  button_width, button_height, 'tooltp'); ROW+=1
  Draw.PushButton('Normal', SEL_LINKED_NORMAL, button_width*COLUM, button_height*ROW,  button_width, button_height, 'tooltp'); ROW+=1
  Draw.PushButton('Area', SEL_LINKED_AREA, button_width*COLUM, button_height*ROW,  button_width, button_height, 'tooltp'); ROW+=1
  BGL.glRasterPos2i(txtBuffer + button_width*COLUM, txtBuffer + button_height*ROW)
  Draw.Text("Sel Linked", fontsize)	
  
  
  
  ROW+=1
  '''
  ROW = 0
  COLUM+=1
  '''
  Draw.PushButton('Image', SEL_SAME_IMAGE, button_width*COLUM, button_height*ROW,  button_width, button_height, 'tooltp'); ROW+=1
  Draw.PushButton('Mat', SEL_SAME_MAT, button_width*COLUM, button_height*ROW,  button_width, button_height, 'tooltp'); ROW+=1
  Draw.PushButton('Normal', SEL_SAME_NORMAL, button_width*COLUM, button_height*ROW,  button_width, button_height, 'tooltp'); ROW+=1
  Draw.PushButton('Planer', SEL_SAME_COPLANER, button_width*COLUM, button_height*ROW,  button_width, button_height, 'tooltp'); ROW+=1
  Draw.PushButton('Area', SEL_SAME_AREA, button_width*COLUM, button_height*ROW,  button_width, button_height, 'tooltp'); ROW+=1
  BGL.glRasterPos2i(txtBuffer + button_width*COLUM, txtBuffer + button_height*ROW)
  Draw.Text("Sel Same", fontsize)	
  
  # Draw limit
  ROW = 0
  COLUM+=1
  



#------------------------------------------------------------------------------#
#                             event handling code                              #
#------------------------------------------------------------------------------#
def handle_event(evt, val):
  #print evt, val
  if (evt in (Draw.ESCKEY, Draw.QKEY) and not val):
    Draw.Exit()

# This recieves the event index, call a function from here depending on the event.
def handle_button_event(evt):
  print evt
  mesh, af = getActMeshAndFace()
  if mesh != None:
    if evt == SET_MAT:
      setMat(mesh)
    elif evt == FLIP_SEL_FACES:
      flipSelFaces(mesh)
    elif evt == DEL_SEL_FACES:
      delSelFaces(mesh)
    elif evt == COPY_ACT_MAT:
      copyActMat(mesh, af)    
    elif evt == COPY_ACT_IMG:
      copyActImg(mesh, af)
    elif evt == SEL_LINKED_IMAGE:
      selectLinkedMulti(mesh, af, 'image')
    elif evt == SEL_LINKED_MAT:
      selectLinkedMulti(mesh, af, 'mat')    
    elif evt == SEL_LINKED_NORMAL:
      selectLinkedMulti(mesh, af, 'normal')
    elif evt == SEL_LINKED_AREA:
      selectLinkedMulti(mesh, af, 'area')    
    elif evt == SEL_SAME_AREA:
      selSameArea(mesh, af)    
    elif evt == SEL_SAME_IMAGE:
      selSameImage(mesh, af)   
    elif evt == SEL_SAME_MAT:
      selSameMat(mesh, af)       
    elif evt == SEL_SAME_NORMAL:
      selSameNormal(mesh, af)   
    elif evt == SEL_SAME_COPLANER:
      selSameCoplaner(mesh, af)      
    
    # update the mesh
    mesh.update()

Draw.Register(draw_gui, handle_event, handle_button_event) 
  