def computeLocationAs2D(j):
	#STEP 0: Copy initial points so they never drift with repeated transformations
	#STEP 1: Translate to make point 1 the origin
	#STEP 2: Rotate around Y axis till point 2 lies in Z=0 plane
	#STEP 3: Rotate around Z axis till point 2 lies in Y=0 plane (now must be on the X-axis)
	#STEP 4: Rotate around X axis till point 3 lies in Z=0 plane
	#STEP 4: Solve for X and Y 
	#STEP 5: Build the solution
	#STEP 6: Back out all transformations

	#STEP 0:	
	locA,locB,locC = copyAllJoints(j)	
	#STEP 1: 
	step1Inverse = jgh.moveToOrigin(locA,[locA,locB,locC])
	#STEP 2:
	step2Inverse = jgh.rotateYToXYPlane(locB,[locA,locB,locC])	 
	#STEP 3:
	step3Inverse = jgh.rotateZToXZPlane(locB,[locA,locB,locC])
	#STEP 4:
	if(j.farOrNear == 'n'):
		yIsPositive = False
	else:
		yIsPositive = True
	step4Inverse = jgh.rotateXToXYPlane(locC,[locA,locB,locC],yIsPos=yIsPositive)
	#STEP 5:
	x,y = jgh.solveForXY(locA,j.spanA.l,locB,j.spanB.l)	
	#BUILD SOLUTION	
	locD = location(x,y,0)	
	#STEP 4-inverse:
	jgh.rotateXToXYPlane_reverse(locD,step4Inverse)
	#STEP 3-inverse:
	jgh.rotateZToXZPlane_reverse(locD,step3Inverse)	
	#STEP 2-inverse:
	jgh.rotateYToXYPlane_reverse(locD,step2Inverse)	
	#STEP 1-inverse:
	jgh.moveToOrigin_reverse(locD,step1Inverse)	
	#RETURN: The main function will set this object to have the location of locD
	return locD
def checkForCollisionPlane(lineA,lineB,colLine,minDistance,debugPrint=False):
	if lineA == 'NULL' or lineB == 'NULL' or colLine == 'NULL':
		return False
	#STEP 1: Copy joints
	#STEP 2: Move so that plane orig is 0,0,0
	#STEP 3: Rotate around Y axis till plane pointA is in XY-plane
	#STEP 4: Rotate around Z axis till plane pointA in X-axis
	#STEP 5: Rotate around X axis till plane pointB is in XY-plane
	#STEP 6: Near edge: For each line end, check distance to Z, if less than min distance, check the point there
	#STEP 7: Clear cross: If the line begins and end on opposite sides of Z=0, solve for Z=0 and get the X,Y	
	#STEP 8: Check if the angle from origin to this new point is smaller than the angle to plane pointB
	#STEP 9: Check if the angle from planePointA to this new point is smaller than the angle from plane pointA to plane pointB

	#STEP 1
	plo,pla,plb,la,lb = jgh.copyPoints(lineA[0],lineA[1],lineB[1],colLine[0],colLine[1])
	allPoints = [plo,pla,plb,la,lb]	
	#STEP 2
	step2Inverse = jgh.moveToOrigin(plo,allPoints)
	if(debugPrint):
		print "step 2"	
		for pt in allPoints:
			print pt.toString()
	#STEP 3:
	step3Inverse = jgh.rotateYToXYPlane(pla,allPoints)	 
	if(debugPrint):
		print "step 3"	
		for pt in allPoints:
			print pt.toString()
	#STEP 4:
	step4Inverse = jgh.rotateZToXZPlane(pla,allPoints)
	if(debugPrint):
		print "step 4"	
		for pt in allPoints:
			print pt.toString()
	#STEP 5:
	step5Inverse = jgh.rotateXToXYPlane(plb,allPoints,yIsPos=True)
	if(debugPrint):
		print "step 5"	
		for pt in allPoints:
			print pt.toString()
	
	ptsToCheck = []
	#STEP 6:
	if (math.fabs(la.z)-minDistance) < 0.0: #No need to handle near-miss/hit with a fluff factor since we already have the standoff distance. 
		tmp = la.copy()
		tmp.z = 0.0	
		ptsToCheck.append(tmp)
		if debugPrint:
			print "end point was close to plane"
	if (math.fabs(lb.z)-minDistance) < 0.0: #No need to handle near-miss/hit with a fluff factor since we already have the standoff distance. 
		tmp = lb.copy()
		tmp.z = 0.0	
		ptsToCheck.append(tmp)
		if debugPrint:
			print "end point was close to plane"
	#STEP 7:
	if (la.z < 0.0 and lb.z > 0.0) or (la.z > 0.0 and lb.z < 0.0):
		#N(X1-X2)+X2 = X
		#N(Y1-Y2)+Y2 = Y
		#N(Z1-Z2)+Z2 = 0
		tmpX = lb.x + (-lb.z/(la.z-lb.z))*(la.x-lb.x)
		tmpY = lb.y + (-lb.z/(la.z-lb.z))*(la.y-lb.y)
		tmp = location(tmpX,tmpY,0.0)
		ptsToCheck.append(tmp)
		if debugPrint:
			print "line goes clear through the plane"
	#STEP 8,9:
	for pt in ptsToCheck:
		origAngleMax = math.atan2(plb.y,plb.x)
		origAngleFound = math.atan2(pt.y,pt.x)
		farAngleMax = math.atan2(plb.y,(pla.x-plb.x))	
		farAngleFound = math.atan2(pt.y,(pla.x-pt.x))
		if(origAngleFound > 0.0 and origAngleFound < origAngleMax and farAngleFound > 0.0 and farAngleFound < farAngleMax):
			if debugPrint:
				"line goes through the surface"	
			return True
		if debugPrint:
			print "STEP 8,9: Not a hit. point:%s, origAngleMax:%f, origAngleFound:%f, farAngleMax:%f, farAngleFound:%f"%(pt.toString(),origAngleMax,origAngleFound,farAngleMax,farAngleFound)
	return False
def test_joint_locationComputation():
	p = True
	#Test case: Rotation
	l1 = location(0,1.0/math.sqrt(2),1.0/math.sqrt(2)) #Y=1, Z=1
	l2 = location(0,1.0/math.sqrt(2),-1.0/math.sqrt(2))
	inv = jgh.rotateXToXYPlane(l1,[l2],yIsPos=True)
	if(l2.z < -1.1 or l2.z > -0.9 or l2.y > 0.1 or l2.y < -0.1 or l2.x > 0.1 or l2.x < -0.1):
		p = False
		print "ERROR on test case 1a: %s"%l2.toString()
	jgh.rotateXToXYPlane_reverse(l2,inv)
	if(l2.z	< -0.8 or l2.z > -0.7 or l2.y > 0.8 or l2.y < 0.7):
		p = False
		print "ERROR on test case 1b: %s"%l2.toString()
	inv = jgh.rotateXToXYPlane(l1,[l2],yIsPos=False)
        if(l2.z < 0.9 or l2.z > 1.1 or l2.y > 0.1 or l2.y < -0.1 or l2.x > 0.1 or l2.x < -0.1):
                p = False
                print "ERROR on test case 1c: %s"%l2.toString()

	#Test case: Rotation
	l1 = location(0,100,-1)
	l2 = location(-1,-100,0)
	inv = jgh.rotateYToXYPlane(l1,[l2])
	if(l2.z > -0.9 or l2.z < -1.1 or l2.x > 0.1 or l2. x < -0.1):
		p = False
		print "ERROR on test case 2: %s"%l2.toString()	
	
	#Test case: Rotation
	l1 = location(-1.0,-1.0,0.0)
	l2 = location(1.0,-1.0,0)
	inv = jgh.rotateZToXZPlane(l1,[l2])
	if(l2.z < -0.1 or l2.z > 0.1 or l2.x < -0.1 or l2.x > 0.1 or l2.y > math.sqrt(2)+0.01 or l2.y < math.sqrt(2)-0.01):
		p = False
		print "ERROR on test case 3: %s"%l2.toString()	

	#Test case: Line
	j = joint()
	j.jointA = location(0,4,0)
	j.jointB = location(1,1,1)
	j.spanA = span( 2.0)
	j.computeLocation()
	if(j.x > 0.61 or j.x < 0.60 or j.y > 2.2 or j.y < 2.1 or j.z > 0.61 or j.z < 0.60):
		p = False
		print "ERROR on test case 4: %s"%j.toString()

	#Test case: 2D joint
	j = joint()
	j.jointA = location(1.0,1.0,0.0)
	j.jointB = location(0.0,1.0,0.0)	
	j.jointC = location(0.0,1.0,0.0)
	j.spanA = span(math.sqrt(2.0))
	j.spanB = span(1.0)
	j.farOrNear = 'f'
	j.computeLocation()
	if(j.x > 0.01 or j.x < -0.01 or j.y > 1.01 or j.y < -0.99 or j.z > 1.01 or j.z < 0.99):
		p = False
		print "ERROR on test case 5: %s"%j.toString()
	
	#Test case: 2D joint
	j = joint()
	j.jointA = location(0.0,1.0,0.0)	
	j.jointB = location(4.0,1.0,0.0)	
	j.jointC = location(0.5,6.0,-2.0)
	j.spanA = span(4.0)
	j.spanB = span(4.0)
	j.farOrNear = 'f'
	j.computeLocation()
	if(j.x > 2.01 or j.x < 1.99 or j.y < -2.22 or j.y > -2.21 or j.z > 1.29 or j.z < 1.285):
		p = False
		print "ERROR on test case 6: %s"%j.toString()

	#Test case: 2D joint
	j = joint()
	j.jointA = location(0.0,1.0,0.0)	
	j.jointB = location(4.0,1.0,0.0)	
	j.jointC = location(0.5,6.0,-2.0)
	j.spanA = span(4.0)
	j.spanB = span(4.0)
	j.farOrNear = 'n'
	j.computeLocation()
	if(j.x > 2.01 or j.x < 1.99 or j.y > 4.22 or j.y < 4.21 or j.z < -1.29 or j.z > -1.285):
		p = False
		print "ERROR on test case 7: %s"%j.toString()

	#Test case: 3D joint
	j = joint()
	j.jointA = location(1.0,0,0)
	j.jointB = location(0.0,1.0,0.0)
	j.jointC = location(0.0,0.0,1.0)
	j.spanA = span(1.0)
	j.spanB = span(math.sqrt(1+1+1))
	j.spanC = span(1.0)
	j.computeLocation()
	if(j.x > 1.01 or j.x < 0.99 or j.y > 0.01 or j.y < -0.01 or j.z > 1.01 or j.z < 0.99):
		p = False
		print "ERROR on test case 8: %s"%j.toString() 

	#Test case: 3D joint
	j = joint()
	j.jointA = location(0.0,2.0,2.0)
	j.jointB = location(-1.0,1.0,0.0)
	j.jointC = location(4.0,0.0,0.0)
	j.spanA = span(4.0)
	j.spanB = span(3.0)
	j.spanC = span(3.0)
	j.computeLocation()
	if(j.x > 1.198 or j.x < 1.19 or j.y > -1.013 or j.y < -1.014 or j.z > -0.34 or j.z < -0.35):
		p = False
		print "ERROR on test case 9: %s"%j.toString() 

	#Test case: 3D joint
	j = joint()
	j.jointA = location(0,0,0)
	j.jointB = location(1,0,0)
	j.jointC = location(.5,-math.sqrt(3)/2.0,0.0)
	j.spanA = span(1.0)
	j.spanB = span(math.sqrt(2.0))
	j.spanC = span(math.sqrt(2.0))
	j.computeLocation()
	if(j.x > 0.01 or j.x < -0.01 or j.y > 0.01 or j.y < -0.01 or j.z > -0.99 or j.z < -1.01):
                p = False
                print "ERROR on test case 10: %s"%j.toString()	
	return p	
def test_joint_locationComputation():
	p = True
	#Test case: Exception in joint naming: not found
	t = False
	s = mfStructure('a',[0,0,0],'b',[0,0,1])
	try:
		s.add2DJoint('c','a',1,'x',1,[0,0,10],'f')	
	except Exception_UnknownJoint:
		t = True
	if t == False:
		print "ERROR on test case -1: Joint naming"
		p = False
	
	#Test case: Exception in joint naming: Already exists
	t = False
	s = mfStructure('a',[0,0,0],'b',[0,0,1])
	try:
		s.add2DJoint('b','a',1,'b',1,[0,0,10],'f')	
	except Exception_DuplicateNamedJoint:
		t = True
	if t == False:
		print "ERROR on test case 0: Joint naming"
		p = False
	
	#Test case: Rotation
	l1 = location(0,1.0/math.sqrt(2),1.0/math.sqrt(2)) #Y=1, Z=1
	l2 = location(0,1.0/math.sqrt(2),-1.0/math.sqrt(2))
	inv = jgh.rotateXToXYPlane(l1,[l2],yIsPos=True)
	if(l2.z < -1.1 or l2.z > -0.9 or l2.y > 0.1 or l2.y < -0.1 or l2.x > 0.1 or l2.x < -0.1):
		p = False
		print "ERROR on test case 1a: %s"%l2.toString()
	jgh.rotateXToXYPlane_reverse(l2,inv)
	if(l2.z	< -0.8 or l2.z > -0.7 or l2.y > 0.8 or l2.y < 0.7):
		p = False
		print "ERROR on test case 1b: %s"%l2.toString()
	inv = jgh.rotateXToXYPlane(l1,[l2],yIsPos=False)
        if(l2.z < 0.9 or l2.z > 1.1 or l2.y > 0.1 or l2.y < -0.1 or l2.x > 0.1 or l2.x < -0.1):
                p = False
                print "ERROR on test case 1c: %s"%l2.toString()

	#Test case: Rotation
	l1 = location(0,100,-1)
	l2 = location(-1,-100,0)
	inv = jgh.rotateYToXYPlane(l1,[l2])
	if(l2.z > -0.9 or l2.z < -1.1 or l2.x > 0.1 or l2. x < -0.1):
		p = False
		print "ERROR on test case 2: %s"%l2.toString()	
	
	#Test case: Rotation
	l1 = location(-1.0,-1.0,0.0)
	l2 = location(1.0,-1.0,0)
	inv = jgh.rotateZToXZPlane(l1,[l2])
	if(l2.z < -0.1 or l2.z > 0.1 or l2.x < -0.1 or l2.x > 0.1 or l2.y > math.sqrt(2)+0.01 or l2.y < math.sqrt(2)-0.01):
		p = False
		print "ERROR on test case 3: %s"%l2.toString()	

	#Test case: Line
	j = joint()
	j.jointA = location(0,4,0)
	j.jointB = location(1,1,1)
	j.spanA = span( 2.0)
	j.computeLocation()
	if(j.x > 0.61 or j.x < 0.60 or j.y > 2.2 or j.y < 2.1 or j.z > 0.61 or j.z < 0.60):
		p = False
		print "ERROR on test case 4: %s"%j.toString()

	#Test case: 2D joint
	j = joint()
	j.jointA = location(1.0,1.0,0.0)
	j.jointB = location(0.0,1.0,0.0)	
	j.jointC = location(0.0,1.0,0.0)
	j.spanA = span(math.sqrt(2.0))
	j.spanB = span(1.0)
	j.farOrNear = 'f'
	j.computeLocation()
	if(j.x > 0.01 or j.x < -0.01 or j.y > 1.01 or j.y < -0.99 or j.z > 1.01 or j.z < 0.99):
		p = False
		print "ERROR on test case 5: %s"%j.toString()
	
	#Test case: 2D joint
	j = joint()
	j.jointA = location(0.0,1.0,0.0)	
	j.jointB = location(4.0,1.0,0.0)	
	j.jointC = location(0.5,6.0,-2.0)
	j.spanA = span(4.0)
	j.spanB = span(4.0)
	j.farOrNear = 'f'
	j.computeLocation()
	if(j.x > 2.01 or j.x < 1.99 or j.y < -2.22 or j.y > -2.21 or j.z > 1.29 or j.z < 1.285):
		p = False
		print "ERROR on test case 6: %s"%j.toString()

	#Test case: 2D joint
	j = joint()
	j.jointA = location(0.0,1.0,0.0)	
	j.jointB = location(4.0,1.0,0.0)	
	j.jointC = location(0.5,6.0,-2.0)
	j.spanA = span(4.0)
	j.spanB = span(4.0)
	j.farOrNear = 'n'
	j.computeLocation()
	if(j.x > 2.01 or j.x < 1.99 or j.y > 4.22 or j.y < 4.21 or j.z < -1.29 or j.z > -1.285):
		p = False
		print "ERROR on test case 7: %s"%j.toString()

	#Test case: 3D joint
	j = joint()
	j.jointA = location(1.0,0,0)
	j.jointB = location(0.0,1.0,0.0)
	j.jointC = location(0.0,0.0,1.0)
	j.spanA = span(1.0)
	j.spanB = span(math.sqrt(1+1+1))
	j.spanC = span(1.0)
	j.computeLocation()
	if(j.x > 1.01 or j.x < 0.99 or j.y > 0.01 or j.y < -0.01 or j.z > 1.01 or j.z < 0.99):
		p = False
		print "ERROR on test case 8: %s"%j.toString() 

	#Test case: 3D joint
	j = joint()
	j.jointA = location(0.0,2.0,2.0)
	j.jointB = location(-1.0,1.0,0.0)
	j.jointC = location(4.0,0.0,0.0)
	j.spanA = span(4.0)
	j.spanB = span(3.0)
	j.spanC = span(3.0)
	j.computeLocation()
	if(j.x > 1.198 or j.x < 1.19 or j.y > -1.013 or j.y < -1.014 or j.z > -0.34 or j.z < -0.35):
		p = False
		print "ERROR on test case 9: %s"%j.toString() 

	#Test case: 3D joint
	j = joint()
	j.jointA = location(0,0,0)
	j.jointB = location(1,0,0)
	j.jointC = location(.5,-math.sqrt(3)/2.0,0.0)
	j.spanA = span(1.0)
	j.spanB = span(math.sqrt(2.0))
	j.spanC = span(math.sqrt(2.0))
	j.computeLocation()
	if(j.x > 0.01 or j.x < -0.01 or j.y > 0.01 or j.y < -0.01 or j.z > -0.99 or j.z < -1.01):
                p = False
                print "ERROR on test case 10: %s"%j.toString()	
	return p