#!BPY

""" Registration info for Blender menus
Name: 'Vertex Align'
Blender: 248
Group: 'Mesh'
Tooltip: 'align selected points inside the mesh'
"""

__author__ = "Sammler Rene"
__url__ = ("http://www.blender.org", "http://www.sammler-mediengestaltung.com")
__version__ = "v070324"

__bpydoc__ = """\
***** BEGIN GPL LICENSE BLOCK *****

This program is free software; you can redistribute it and/or<br>
modify it under the terms of the GNU General Public License<br>
as published by the Free Software Foundation; either version 2<br>
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 *****
--------------------------------------------------------------------------

This script allows to align vertex points of a mesh to each other vertx of the same object.<br>
Aditionally you can fix the vertex points, so that they keep there internal structure due to the alignement process.
 (the fixed vertex ponts will be handled like one single object).
"""


import Blender
from Blender.Draw import *
from Blender.BGL import *
from Blender.Types import *
from Blender.NMesh import *
from Blender.Window import *

vert = []
ref_vert = []

target_str = "target point: empty"
ref_str = "reference point: empty"

tgl_x = Create(0)
tgl_y = Create(0)
tgl_z = Create(0)
mode_01 = Create(0)
slected_mesh = 0
failur = ""

def type_check(obj_list):
	global failur
	ret = 0
	if len(obj_list) == 0: 
		failur = "ERROR: Select a Meshobject first!!"
		ret = 1
	elif type(obj_list[0].getData()) != NMeshType: 
		failur = "ERROR: Select a Meshobject first!!"
		ret = 1
	else: failur = ""
	return ret

def get_selected_verts(vert_lst):
	ret = []
	for i in range(0, len(vert_lst)):
		if vert_lst[i].sel == 1:
			ret.append(i)
	return ret

def get_max_vals(index, vlst):
	global flag, failur
	if len(vlst) > index:
		vertex = vlst[index]
		if flag == 0:
			min[0] = vertex.co[0]
			min[1] = vertex.co[1]
			min[2] = vertex.co[2]					
			max[0] = vertex.co[0]
			max[1] = vertex.co[1]
			max[2] = vertex.co[2]						
			flag = 1
		else:
			if vertex.co[0] < min[0]: min[0] = vertex.co[0]
			if vertex.co[1] < min[1]: min[1] = vertex.co[1]
			if vertex.co[2] < min[2]: min[2] = vertex.co[2]					
			if vertex.co[0] > max[0]: max[0] = vertex.co[0]
			if vertex.co[1] > max[1]: max[1] = vertex.co[1]
			if vertex.co[2] > max[2]: max[2] = vertex.co[2]	
	else:
		print "Index %d is for this object not available! \nYou should asign a new target or reference point!"%index
		failur = "Warning: check console"
	

def calc_median(vlst, indexlst):
	global min, max, flag
	min = [0,0,0]
	max = [0,0,0]
	ret = [0,0,0]
	flag = 0
	
	[get_max_vals(index, vlst) for index in indexlst]								
		
	for i in range(0, 3):
		tmp = max[i] - min[i]
		if tmp == 0: ret[i] = max[i]
		else: ret[i] = min[i] + tmp / 2	
	
	return ret

def move_points_median(vertex,x,y,z):
	if vertex.sel == 1:
		if tgl_x.val == 1: vertex.co[0] = vertex.co[0] + x
		if tgl_y.val == 1: vertex.co[1] = vertex.co[1] + y
		if tgl_z.val == 1: vertex.co[2] = vertex.co[2] + z
	
def move_points_fix(vertex, target):
	if vertex.sel == 1:
		if tgl_x.val == 1: vertex.co[0] = target[0]
		if tgl_y.val == 1: vertex.co[1] = target[1]
		if tgl_z.val == 1: vertex.co[2] = target[2]	

def BEvent(evt):
	global ref_vert,vert,slected_mesh
	global failur, target_str, ref_str
	
	if evt == 8:
		edit_mode = EditMode()
		if EditMode(): EditMode(0)
		obj_lst = Blender.Object.GetSelected()
		if type_check(Blender.Object.GetSelected()) == 0:
			selected_mesh = Blender.NMesh.GetRawFromObject(str(obj_lst[0]).split("\"")[1])
			vert = get_selected_verts(selected_mesh.verts)
			if len(vert) > 0:
				if len(vert) == 1:
					target_str = "target point: ok (single[%d])"%vert[0]
					Blender.Redraw()
				else:
					target_str = "target point: ok (median[%d])"%len(vert)
			else:
				target_str = "target point: empty"					
		if edit_mode: EditMode(1)

	elif evt == 9:
		edit_mode = EditMode()
		if EditMode(): EditMode(0)
		obj_lst = Blender.Object.GetSelected()	
		if type_check(Blender.Object.GetSelected()) == 0:
			selected_mesh = Blender.NMesh.GetRawFromObject(str(obj_lst[0]).split("\"")[1])
			vert_lst = selected_mesh.verts
	
			if len(vert) > 0:
				vert_median = calc_median(vert_lst, vert)			
				
				if mode_01.val == 1:				
					
					ref_vert_median = calc_median(vert_lst, ref_vert)		
					tmp_x = vert_median[0] - ref_vert_median[0] 
					tmp_y = vert_median[1] - ref_vert_median[1]
					tmp_z = vert_median[2] - ref_vert_median[2]					
					[move_points_median(vertex, tmp_x, tmp_y, tmp_z) for vertex in vert_lst]
				else:
					[move_points_fix(vertex, vert_median) for vertex in vert_lst]
			else:
				failur = "The target point is empty!"
		
			selected_mesh.verts=vert_lst
			Blender.NMesh.PutRaw(selected_mesh, selected_mesh.name)	
			if failur == "Warning: check console":
				print "----------------------------------------------------"
		if edit_mode: EditMode(1)	
					
	elif evt == 7:
		edit_mode = EditMode()
		if EditMode(): EditMode(0)
		obj_lst = Blender.Object.GetSelected()
		if type_check(Blender.Object.GetSelected()) == 0:
			selected_mesh = Blender.NMesh.GetRawFromObject(str(obj_lst[0]).split("\"")[1])
			ref_vert = get_selected_verts(selected_mesh.verts)
			if len(ref_vert) > 0:
				if len(ref_vert) == 1:
					ref_str = "reference point: (single[%d])"%ref_vert[0]
				else:
					ref_str = "reference point: (median[%d])"%len(ref_vert)	
			else:
				ref_str = "reference point: empty"							
		if edit_mode: EditMode(1)	

	elif evt == 4:
		if mode_01.val == 1: 
			edit_mode = EditMode()
			if EditMode(): EditMode(0)
			obj_lst = Blender.Object.GetSelected()
			if type_check(Blender.Object.GetSelected()) == 0:
				selected_mesh = Blender.NMesh.GetRawFromObject(str(obj_lst[0]).split("\"")[1])
				ref_vert = get_selected_verts(selected_mesh.verts)
				if len(ref_vert) > 0:
					if len(ref_vert) == 1:
						ref_str = "reference point: (single[%d])"%ref_vert[0]
					else:
						ref_str = "reference point: (median[%d])"%len(ref_vert)
				else:
					ref_str = "reference point: empty"
			if edit_mode: EditMode(1)			
		else:
			ref_vert = []
			ref_str = "reference point: empty"
										
	elif evt == 10:
			Exit()
			return
	
	Blender.Redraw()

def Event(evt, val):
	if evt == QKEY:
		Exit()
		return

def  GUI ():
	global tgl_x, tgl_y, tgl_z, mode_01, ref_vert, vert, failur

	glColor3f(0.7, 0.7, 0.7)
	glRecti(15, 35, 320, 110)
	glColor3f(0.9, 0.9, 0.9)
	glRecti(15, 15, 320, 35)
	glColor3f(0.65, 0.65, 0.65)	
	glRecti(15, 110, 320, 130)
	glColor3f(0.0, 0.0, 0.0)
	glRecti(15, 54, 320, 56)
	
	glRasterPos2f(20,117)		
	Text("Vertex Align (v 07 03 24)")	

	tgl_x = Toggle("X", 1, 20, 85, 60, 20, tgl_x.val, "align to x direction (on/off)")
	tgl_y = Toggle("Y", 2, 85, 85, 60, 20, tgl_y.val, "align to y direction (on/off)")
	tgl_z = Toggle("Z", 3, 150, 85, 60, 20, tgl_z.val, "align to z direction (on/off)")

	mode_01 = Toggle("Fix Verts", 4, 255, 85, 60, 20, mode_01.val, "preserve the point structure due to the alignment")

	select = Button("Select Target", 8, 20, 60, 90, 20, "target point of alignment")
	select3 = Button("Select Reference", 7, 120, 60, 90, 20, "reference point for the alignment of a point structure")
	select2 = Button("Align", 9, 215, 60, 35, 45, "align the marked points in order of the selected options")
	
	quit = Button("Quit", 10, 255, 60, 60, 20, "quit the script")

	glColor3f(0.15,0.15,0.15)

	glRasterPos2f(20,41)	
	Text(target_str, "small")

	glRasterPos2f(170,41)	
	Text(ref_str, "small")

	glRasterPos2f(35,5)	
	Text("created by Rene Sammler (sammler-mediengestaltung.com) (v 07 03 24)", "tiny")
	glColor3f(1, 0.0, 0.0)
	glRasterPos2f(20,20)	
	Text(failur)
	
Register(GUI, Event, BEvent)
Blender.Redraw(1)