#!BPY

"""
Name: 'Lengthen'
Blender: 248
Group: 'Mesh'
Tip: 'Position vertices at a specified distance'
"""

__author__ = 'Pedro Caeiro (madcello)'
__version__ = '2.41'
__url__ = ["http://wiki.blender.org/index.php/Scripts/Manual/Mesh/Lengthen", "http://blenderartists.org/forum/showthread.php?t=67473&page=2"]

__bpydoc__ = """\ 
To use Lengthen we must select two vertexes and then choose the new distance between those selected vertexes. 
The orientation of these vertexes does not change, only their distance.
"""

#########################################################
# Lengthen version 0.22
# Original script by Pedro Caeiro
# Modified by Levi Schooley (aka. reD_Fox)
# Updated by Pedro Caeiro
#########################################################

# $Id: lengthen_021.py,v 0.21 from 10/00/2006 by Pedro Caeiro (madcello)$
#
# ***** 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 *****
# --------------------------------------------------------------------------

import Blender
from Blender import Draw, BGL, Window, Object, Mathutils
from Blender.BGL import *

### MAIN VARIABLES ###
mode = Window.EditMode()
err = "None"

global index1, index2
name1 = ""
name2 = ""

global number_label

global delta, Vector1, Vector2

Ready = 0

### SCRIPTLINK VARIABLES ###
try:
	Blender.__vertices__ = {}
except:
    foobar = Blender.__vertices__

scene = Blender.Scene.GetCurrent()

EVENT = "Redraw"
NAME = "Length_points"

own_vert1 = "vert1"
own_vert2 = "vert2"

is_runing = 0

### GUI VARIABLES ###
EVENT_EXIT = 10
EVENT_PICK_1 = 11
EVENT_PICK_2 = 13
EVENT_DISTANCE = 14
EVENT_APPLY = 15
EVENT_PERCENTAGE = 16
EVENT_FIX_1 = 17
EVENT_FIX_2 = 18
EVENT_GROUP = 19
EVENT_DISTINFO = 20

Percent = Draw.Create(0)
Object_Distance = Draw.Create(3.0000)

Fix1 = Draw.Create(1)
Fix2 = Draw.Create(0)

dist = ""
deltaX = ""
deltaY = ""
deltaZ = ""

####### GUI DRAWING #######
def draw():
	global Percent, Object_Distance, Fix1, Fix2, err
	global number_label, NAME, EVENT, is_runing
	global index1, index2, name1, name2
	global own_vert1, own_vert2
	global dist, deltaX, deltaY, deltaZ

	h = 20
	w = 120
	x = 8
	y = 8

	area = Window.GetAreaSize()
	if area[0] < x + w:
		x = 1
		w = area[0]-4
	if area[1] < y+h*7:
		y = 0
		h = area[1]/7

	##***** LABEL IN NUMBER BUTTON
	if Percent.val:
		number_label = "Percent"
	else:
		number_label = "Distance"
	
    ##*****THE DELTAS TEXT
	if name1 != "" and name2 != "":
		err, vec1, vec2 = get2Vecs(index1, index2, name1, name2)
		if err == "None":
			d = vec2 - vec1
			dx = vec2[0] - vec1[0]
			dy = vec2[1] - vec1[1]
			dz = vec2[2] - vec1[2]
			
			dist = str(round(d.length,4))
			deltaX = str(round(dx,4))
			deltaY = str(round(dy,4))
			deltaZ = str(round(dz,4))
		else:
			Blender.__vertices__ = {}
			index1 = None
			index2 = None
			name1 = ""
			name2 = ""
    
    ##*****PICKED VERTICES TEXT
	s1 = s2 = t1 = t2 = ""
	if name1 != "":
		s1 = "*"
		t1 = "Vertice #"+str(index1)+" in "+name1
	if name2 != "":
		s2 = "*"
		t2 = "Vertice #"+str(index2)+" in "+name2

    ##### REAL DRAW #####
	BGL.glClearColor(0.500000, 0.500000, 0.500000, 0.0)
	BGL.glClear(GL_COLOR_BUFFER_BIT)

	if err == "None":
		BGL.glColor3f(0.0, 0.0, 0.0)
	else:
		BGL.glColor3f(1.0, 1.0, 0.0)
	BGL.glRasterPos2i(x, y+5)
	Draw.Text("Error: " + err)
	
	Draw.Button("Apply", EVENT_APPLY, x, y+h, w/2, h, "Apply distance or percentage, if active")
	Draw.Button("to group", EVENT_GROUP, x+w/2, y+h, w-w/2, h, "Grab vertices group")
	Draw.Button("Exit", EVENT_EXIT, x+w, y+h, w-w/2, h, "Exit the script")
	Percent = Draw.Toggle("% (Percentage)",EVENT_PERCENTAGE, x, y+h*2, w, h, Percent.val, "Use a percentage of original distance for new distance")
	Object_Distance = Draw.Number(number_label, EVENT_DISTANCE, x, y+h*3, w, h, Object_Distance.val, -250.0000, 250.0000, "Set the new distance, or a multiplication factor (percent)")
	Fix2 = Draw.Toggle("Fix", EVENT_FIX_2, x, y+h*4, w/4, h, Fix2.val, "Fix the 2nd vertice")
	Draw.Button(s2 + "Pick 2", EVENT_PICK_2, x+w/4, y+h*4, w-w/4, h, "Select the 2nd vertice")
	BGL.glColor3f(0.9, 0.9, 0.0)
	BGL.glRasterPos2i(x+w+5, y+h*4+5)
	Draw.Text(t2)
	Fix1 = Draw.Toggle("Fix", EVENT_FIX_1, x, y+h*5, w/4, h, Fix1.val, "Fix the 1st vertice")
	Draw.Button(s1 + "Pick 1", EVENT_PICK_1, x+w/4, y+h*5, w-w/4, h, "Select the 1st vertice")
	BGL.glColor3f(0.0, 0.9, 0.0)
	BGL.glRasterPos2i(x+w+5, y+h*5+5)
	Draw.Text(t1)
		
	BGL.glColor3f(0.0, 0.0, 0.0)
	BGL.glRasterPos2i(x, y+h*9+5)
	Draw.Text("Distance:" + dist)
	Dist_info = Draw.Button("Copy distance", EVENT_DISTINFO, x, y+h*10+5, w, h, "Copy value to Distance parameter")
	
	BGL.glRasterPos2i(x, y+h*8+5)
	Draw.Text("DeltaX: " + deltaX)

	BGL.glRasterPos2i(x, y+h*7+5)
	Draw.Text("DeltaY: " + deltaY)
	
	BGL.glRasterPos2i(x, y+h*6+5)
	Draw.Text("DeltaZ: " + deltaZ)
	
	### LINK THE SCRIPT THAT DRAWS ON 3D WINDOW###
	if is_runing == 0:
		script = BGL_script()
		write_script(NAME, script)
		linkar(NAME, EVENT)
		is_runing = 1
		
	else: pass # Is already running
	
### GUI EVENTS ###

def event(event, value):
	if event in [Draw.QKEY] and not value:
		deslinkar (NAME)
		del Blender.__vertices__
		Blender.Redraw()
		Draw.Exit()

def b_event(event):
	global Object_Distance, Fix1, Fix2, err, mode
	global index1, index2, name1, name2, dist
	global delta, Vector1, Vector2, Ready

	mode = Window.EditMode()
	Window.EditMode(0)
	err = "None"
	
	if event == EVENT_EXIT:
		deslinkar (NAME)
		del Blender.__vertices__
		Blender.Redraw()
		Draw.Exit()
	
	elif event in [EVENT_PICK_1,EVENT_PICK_2]:
		Ready = 0
		
		if Object.GetSelected() == []:
			err = "Please select an object"
		else:
			objecto = Object.GetSelected()[0]
			me = objecto.getData()
			name = objecto.getName()
			if objecto.getType() != "Mesh":
				err = "Non-Mesh object"
			else:
				index = []
			
				for vert in me.verts:
					if vert.sel:
						index.append(vert.index)
				if len(index) != 1:
					err = "Select 1 vertice"
				else:
					if event == EVENT_PICK_1:
						index1 = index[0]
						name1 = name
						
						Blender.__vertices__[own_vert1] = [name1, index[0]]
					else:
						index2 = index[0]
						name2 = name
						
						Blender.__vertices__[own_vert2] = [name2, index[0]]
	
	elif event == EVENT_DISTINFO:
		copyvalue()
	elif event == EVENT_APPLY:
		Distance()
	elif event == EVENT_FIX_1:
		Fix2.val = 0
	elif event == EVENT_FIX_2:
		Fix1.val = 0
		
### GROUP ###		
		
	elif event == EVENT_GROUP:
		to_group()
			
	Draw.Redraw()
	Window.EditMode(mode)

Draw.Register(draw, event, b_event)

########################################
#	MAIN FUNCTIONS
########################################
######## LINK SCRIPT TO SCENE ##########
def write_script(name, script):
	global scene, is_runing
	
	run_scripts = Blender.Text.Get()
	run_list = [txt.name for txt in run_scripts]
	
	written = 0
	if name not in run_list:
		is_runing = 0
		written = 1
	elif run_scripts[run_list.index(name)].asLines()[1] != "#"+str(__version__):
		written = 2
		is_runing = 1
	if written == 1:
		run_this = Blender.Text.New(name)
		run_this.write(script)
		is_runing = 1
	if written == 2:
		run_this = Blender.Text.Get(name)
		run_this.write(script)
		is_runing = 1

def linkar(name, event_type):
	global scene
	try:
		in_use = [script for script in scene.getScriptLinks(type)]
	except: in_use = []
		
	if name not in in_use:
		scene.addScriptLink(name,event_type)
		#Blender.UpdateMenus()
		
def deslinkar(name):
	global scene
	
	try:
		runing = Blender.Text.Get(name)
		runing.clear()
		scene.clearScriptLinks([name])
		Blender.Text.unlink(runing)
		#Blender.UpdateMenus()
		#print "Deslinkou"
	except: print ("Unknown error")

############## ANTIGO ############
def get2Vecs(index1,index2,name1,name2):
	
	vec1 = Mathutils.Vector()
	vec2 = Mathutils.Vector()
	err = "None"

	if name1 == "" or name2 == "":
		err = "You must pick two vertices"
	elif name1 not in [o.name for o in Object.Get()]:
		err = "There is no object named " + name1
	elif name1 != name2 and name2 not in [o.name for o in Object.Get()]:
		err = "There is no object named " + name2
	else:
		me1 = Object.Get(name1).getData()
		if name1 == name2:
			me2 = me1
		else:
			me2 = Object.Get(name2).getData()
		if len(me1.verts) < index1+1 or len(me2.verts) < index2+1:
			err = "Vertice does not exist"
		else:
			vec1 = Mathutils.Vector(me1.verts[index1].co)
			vec2 = Mathutils.Vector(me2.verts[index2].co)
			vec1.resize4D()
			vec2.resize4D()
			
			if name1 != name2:
				
				mat1 = Object.Get(name1).getMatrix()
				mat2 = Object.Get(name2).getMatrix()
				vec1 = vec1 * mat1 #VecMultMat
				vec2 = vec2 * mat2 #VecMultMat
	
			delta = vec2 - vec1
			if delta.length < 1e-8:
				err = "Vertices cannot have the same coordinates"

	return err, vec1, vec2

def Distance():
	global Percent, Fix1, Fix2, dist, err, name1, name2
	global delta, Vector1, Vector2, Ready

	err, vec1, vec2 = get2Vecs(index1,index2,name1,name2)

	
	if err == "None":
		me1 = Object.Get(name1).getData()
		if name1 == name2:
			me2 = me1
		else:
			me2 = Object.Get(name2).getData()

		delta = vec2 - vec1
		
		nova_hipo = Object_Distance.val # QUAL O NOVO COMPRIMENTO
		
		if Percent.val:
			ratio = nova_hipo / 100
		else:
			ratio = nova_hipo / delta.length

		if Fix1.val:
			novo = delta * ratio + vec1
			if name1 != name2:
				mat2 = Object.Get(name2).getInverseMatrix()
				novo = novo * mat2 #VecMultMat
			me2.verts[index2].co[0] = novo[0]
			me2.verts[index2].co[1] = novo[1]
			me2.verts[index2].co[2] = novo[2]
		elif Fix2.val:
			novo = vec2 - delta * ratio
			if name1 != name2:
				mat1 = Object.Get(name1).getInverseMatrix()
				novo = novo * mat1 #VecMultMat
			me1.verts[index1].co[0] = novo[0]
			me1.verts[index1].co[1] = novo[1]
			me1.verts[index1].co[2] = novo[2]
		else:
			delta = (delta * ratio + vec1 - vec2) * 0.5
			novo = vec2 + delta
			if name1 != name2:
				mat2 = Object.Get(name2).getInverseMatrix()
				novo = novo * mat2 #VecMultMat
			me2.verts[index2].co[0] = novo[0]
			me2.verts[index2].co[1] = novo[1]
			me2.verts[index2].co[2] = novo[2]

			novo = vec1 - delta
			if name1 != name2:
				mat1 = Object.Get(name1).getInverseMatrix()
				novo = novo * mat1 #VecMultMat
			me1.verts[index1].co[0] = novo[0]
			me1.verts[index1].co[1] = novo[1]
			me1.verts[index1].co[2] = novo[2]
		
		Vector1 = vec1
		Vector2 = vec2
		Ready = 1
		
		me1.update()
		me2.update()
		Window.EditMode(mode)
		Blender.Redraw()

def to_group():
	global delta, name1, name2, Vector1, Vector2
	
	if Ready == 1:
		
		if Object.GetSelected() == []:
			err = "No vertice group to move"
		else:
			objecto = Object.GetSelected()[0]
			me = objecto.getData()
			name = objecto.getName()
			
			if objecto.getType() != "Mesh":
				err = "Non-Mesh object"
			else:
				index = []
				
				nova_hipo = Object_Distance.val # QUAL O NOVO COMPRIMENTO
				
				if Percent.val:
					ratio = nova_hipo / 100
				else:
					ratio = nova_hipo / delta.length											
				
				for vert in me.verts:
					if vert.sel:
						index.append(vert.index)
				
				for vert in index:
					vec = Mathutils.Vector(me.verts[vert].co)
					vec.resize4D()
					
					if name == name1:
						
						v_trans = vec - Vector2
						v_int = v_trans + Vector1
						
						novo = delta * ratio + v_int
						
						me.verts[vert].co[0] = novo[0]
						me.verts[vert].co[1] = novo[1]
						me.verts[vert].co[2] = novo[2]
					else:
						matriz = objecto.getMatrix()
						vec = vec * matriz #VecMultMat
						
						v_trans = vec - Vector2
						v_int = v_trans + Vector1
						
						inv_matriz = objecto.getInverseMatrix()
						
						novo = delta * ratio + v_int
						novo = novo * inv_matriz
						
						me.verts[vert].co[0] = novo[0]
						me.verts[vert].co[1] = novo[1]
						me.verts[vert].co[2] = novo[2]						
			
			me.update()
			Window.EditMode(mode)
			Blender.Redraw()
	else:
		err = "Apply the rule first (2 vertices)"

def copyvalue():
	if dist != "":
		if Percent.val == 1:
			Percent.val = 0
			Object_Distance.val = float (dist)
		else:
			Object_Distance.val = float (dist)
	else:
		print "There's no value to copy"
		
########
def BGL_script():
	
	script = "#!BPY\n"
	script = script + "#"+str(__version__)+"""
#This script is a part of Lengthen_021.py.
#Author : Pedro Caeiro (madcello)

import Blender
from Blender import BGL, Draw, Window, Object, NMesh, Mathutils
from Blender.BGL import *

scene = Blender.Scene.GetCurrent()
	
v1_owner = ""
v2_owner = ""
	
verts_list = Blender.__vertices__

#################################
def draw_point1(x, y, z):
	glPointSize(5)
	glColor3f(0.0, 1.0, 0.0)
	glBegin(GL_POINTS)
	glVertex3f(x, y, z)
	glEnd()
#################################
def draw_point2(x, y, z):
	glPointSize(5)
	glColor3f(1.0, 1.0, 0.0)
	glBegin(GL_POINTS)
	glVertex3f(x, y, z)
	glEnd()
##########################

####### MATRIXEZ #########
matview = Blender.Window.GetPerspMatrix()

MatPreBuff=[]

for i in range(4):
	for j in range(4):
		MatPreBuff.append(matview[i][j])

MatBuff=BGL.Buffer(GL_FLOAT,16,MatPreBuff)

glLoadIdentity()
glMatrixMode(GL_PROJECTION)
glPushMatrix()
glLoadMatrixf(MatBuff)

#################RRRRRRRR

if verts_list.has_key("vert1"):
	Object1 = Object.Get(verts_list["vert1"][0])
	Ob1 = Object1.getData()
	Ob1_mat = Object.Get(verts_list["vert1"][0]).getMatrix()
	Ve1_idx = verts_list["vert1"][1]
	
	V1 = Mathutils.Vector(Ob1.verts[Ve1_idx].co)
	V1.resize4D()
	V1 = V1 * Ob1_mat #VecMultMat
	V1.resize3D()
		
	X1 = V1[0]	
	Y1 = V1[1]	
	Z1 = V1[2]	
	draw_point1(X1, Y1, Z1)
		
if verts_list.has_key("vert2"):
	Object2 = Object.Get(verts_list["vert2"][0])
	Ob2 = Object2.getData()
	Ob2_mat = Object.Get(verts_list["vert2"][0]).getMatrix()
	Ve2_idx = verts_list["vert2"][1]
	
	V2 = Mathutils.Vector(Ob2.verts[Ve2_idx].co)
	V2.resize4D()
	V2 = V2 * Ob2_mat #VecMultMat
	V2.resize3D()
		
	X2 = V2[0]	
	Y2 = V2[1]	
	Z2 = V2[2]
	draw_point2(X2, Y2, Z2)
	
else:
	pass

glMatrixMode(GL_PROJECTION)
glPopMatrix()
glMatrixMode(GL_MODELVIEW)

	"""
	return script