import Blender

from Blender import Mathutils, Window, Scene, Draw, Mesh

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 mouseViewRay(screen_x, screen_y, localMatrix=None, useMid = False):

	

	# Constant function variables

	p = mouseViewRay.p

	d = mouseViewRay.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 = mouseViewRay.hms

				ortho_d = mouseViewRay.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

				hms[:] = (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[:] = ortho_d[:3]

				



			# 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 = mouseViewRay.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[:] = \

				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[:] = 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[:] = 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

mouseViewRay.d = Vector(0,0,0) # Perspective, 3d

mouseViewRay.p = Vector(0,0,0)

mouseViewRay.fp = Vector(0,0,0)



mouseViewRay.hms = Vector(0,0,0,0) # ortho only 4d

mouseViewRay.ortho_d = Vector(0,0,0,0) # ortho only 4d



def spaceRect():
	'''
	Returns the space rect
	xmin,ymin,width,height
	'''
	
	__UI_RECT__ = Blender.BGL.Buffer(Blender.BGL.GL_FLOAT, 4)
	Blender.BGL.glGetFloatv(Blender.BGL.GL_SCISSOR_BOX, __UI_RECT__) 
	__UI_RECT__ = __UI_RECT__.list
	__UI_RECT__ = int(__UI_RECT__[0]), int(__UI_RECT__[1]), int(__UI_RECT__[2])-1, int(__UI_RECT__[3]) 
	
	return __UI_RECT__

def mouseRelativeLoc2d(__UI_RECT__= None):
	if not __UI_RECT__:
		__UI_RECT__ = spaceRect()
	
	mco = Window.GetMouseCoords()
	if	mco[0] > __UI_RECT__[0] and\
	mco[1] > __UI_RECT__[1] and\
	mco[0] < __UI_RECT__[0] + __UI_RECT__[2] and\
	mco[1] < __UI_RECT__[1] + __UI_RECT__[3]:
	
		return (mco[0] - __UI_RECT__[0], mco[1] - __UI_RECT__[1])
		
	else:
		return None
	




	







	