https://github.com/quantixed/VolumeFinder
Raw File
Tip revision: 708b6d9a96330da323d07708274e68b0a8bf8615 authored by Stephen Royle on 09 March 2017, 11:37:32 UTC
SNR added
Tip revision: 708b6d9
FindingVectorsFromSkeleton.ipf
#pragma TextEncoding = "MacRoman"		// For details execute DisplayHelpTopic "The TextEncoding Pragma"
#pragma rtGlobals=3		// Use modern global access method and strict wave access.

// Menu item for easy execution
Menu "Macros"
	Submenu "Spatial Analysis"
	"MTs2Vectors...",  MTs2Vectors()
	"Remake Report", TidyAndReport()
	"Redo Ellipse Comparison", ReDoElliAnalysis()
	End
End

// Note the execution of MTs2Vectors is slow with many MTs
// Further increases in speed are likely if elli_* and vec_* waves
// are placed into data folders or packed into a 3D wave.
// This has consequences for the remaining programs so has been left the way it is.

// Workflow to load and analyse a dataset from set of TIFFs through to report
Function MTs2Vectors()
	if(ProcessTIFFs() == -1)
		Print "Error"
		Return 0
	endif
	Variable timer = startmstimer
	Polarise()
	segWrapper()
	elliWrapper()
	printf "%g\r", stopmstimer(timer)/1e6
	MakeMaps()
	TidyAndReport()
End

// Workflow to just reanalyse the data
Function ReDoElliAnalysis()
	elliWrapper()
	TidyAndReport()
End

// Loads and processes a folder of TIFFs
Function ProcessTIFFs()
	// kill all windows and waves before we start
	String fullList = WinList("*", ";","WIN:3")
	String name
	Variable i
 
	for(i = 0; i < ItemsInList(fullList); i += 1)
		name = StringFromList(i, fullList)
		DoWindow/K $name		
	endfor
	KillWaves/A/Z
	
	String FileList, ThisFile
	Variable FileLoop
	
	String userResponse
	Prompt userResponse, "Do you have spindle axis waves?", popup, "yes;no;"
	DoPrompt "Axis definition", userResponse
	if (V_flag!=0)
		DoAlert 0, "User pressed cancel"
		Return -1
	endif
	
	if(cmpstr(userResponse,"yes")==0)
		NewPath/O/Q/M="Folder with axis points" axisFolder
		if (V_flag != 0)
			DoAlert 0, "User pressed cancel"
			Return -1
		endif
		LoadWave/W/H/A/C/P=axisFolder "labelWave.ibw"
		LoadWave/W/H/A/C/P=axisFolder "r_p1Wave.ibw"
		LoadWave/W/H/A/C/P=axisFolder "r_cWave.ibw"
		LoadWave/W/H/A/C/P=axisFolder "r_p2Wave.ibw"
	endif
	WAVE/T/Z labelWave
	WAVE/Z r_p1Wave,r_p2Wave
	
	NewPath/O/Q/M="Folder with skeletons" ExpDiskFolder
	if (V_flag != 0)
		DoAlert 0, "User pressed cancel"
		Return -1
	endif
	FileList = IndexedFile(expDiskFolder,-1,".tif")
	Variable nFiles = ItemsInList(FileList)
	Variable /G fileIndex
	ThisFile = StringFromList(0,FileList)
	String baseName = ReplaceString(".Labels0000-labeled-skeletons.tif",ThisFile,"")
	
	Prompt baseName, "Enter baseName"
	DoPrompt "What is the original TIFF stack name?", baseName
	String /G TIFFtitle = baseName
	if (V_flag!=0)
		DoAlert 0, "User pressed cancel"
		Return -1
	endif
	
	Variable pxSize = 12
	Variable zSize = 60
	
	Prompt pxSize, "Pixel size, nm"
	Prompt zSize, "Section interval, nm"
	DoPrompt "Please check", pxSize, zSize
	Variable /G gpxSize = pxSize
	Variable /G gzSize = zSize
	if (V_flag!=0)
		DoAlert 0, "User pressed cancel"
		Return -1
	endif
	
	Variable sp1x = 0
	Variable sp1y = 0
	Variable sp1z = 250
	Variable sp2x = 768
	Variable sp2y = 768
	Variable sp2z = 250
	
	if(cmpstr(userResponse,"yes")==0)
		FindValue/TEXT=baseName labelWave
		i = V_Value
		sp1x = r_p1Wave[i][0]
		sp1y = r_p1Wave[i][1]
		sp1z = r_p1Wave[i][2]
		sp2x = r_p2Wave[i][0]
		sp2y = r_p2Wave[i][1]
		sp2z = r_p2Wave[i][2]
	endif
	
	Prompt sp1x, "X1"
	Prompt sp1y, "Y1"
	Prompt sp1z, "Z1"
	Prompt sp2x, "X2"
	Prompt sp2y, "Y2"
	Prompt sp2z, "Z2"
	DoPrompt "Define centrosome positions, px", sp1x,sp1y,sp1z, sp2x,sp2y,sp2z
	if (V_flag!=0)
		DoAlert 0, "User pressed cancel"
		Return -1
	endif
	
	Variable timer = startmstimer
	
	Make/O spWave={{sp1x,sp2x},{sp1y,sp2y},{sp1z,sp2z}}
	spWave[][0,1] *= pxSize
	spWave[][2] *= zSize
	
	for(FileLoop = 0; FileLoop < nFiles; FileLoop += 1)
		ThisFile=StringFromList(FileLoop, FileList)
		ImageLoad/O/T=tiff/Q/P=expDiskFolder/N=lImage ThisFile
		fileIndex = FileLoop * zSize
		Wave lImage
		if(sum(lImage) > 0)
			Extractor(lImage)
		endif
		KillWaves /Z lImage // should be killed by Extractor()
	endfor
	printf "%g\r", stopmstimer(timer)/1e6
End

// This pulls the skeletons out from each TIFF and sends to TheFitter
///	@param	m0		lImage 2D wave(image)
Function Extractor(m0)
	Wave m0
	
	ImageStats m0
	NVAR /Z nZ = fileIndex	// global variable
	Variable lastMT = V_max
	String wName
	
	Variable i
	
	for(i = 1; i < (lastMT + 1); i += 1) // MT, 1-based
		Duplicate/O/FREE m0, tempXw
		Duplicate/O/FREE m0, tempYw
		tempXw = (m0[p][q] == i) ? p : NaN
		tempYw = (m0[p][q] == i) ? q : NaN
		Redimension/N=(V_npnts) tempXw,tempYw
		WaveTransform zapnans tempXw
		WaveTransform zapnans tempYw
		if(numpnts(tempXw) > 2)
			TheFitter(tempXw,tempYw,i)
		endif
	endfor
	KillWaves m0,tempXw,tempYw
End

// Fits a 2D line to the skeleton and uses this as a vec* wave
///	@param	xW		this is the xWave for fitting
///	@param	yW		this is the yWave for fitting
///	@param	i		passing this variable rather than using another global variable
Function TheFitter(xW,yW,i)
	Wave xW
	Wave yW
	Variable i
	NVAR/Z nZ = fileIndex	// global variable
	NVAR/Z xySize = gpxSize
	WAVE/Z W_coef
	
	CurveFit/Q/NTHR=0 line, yW /X=xW /D
	WAVE /Z fit_tempYw
	if(sum(W_coef) != 0)
		String wName = "vec_" + num2str(nZ) + "_" + num2str(i)
		Make/O/N=(2,3) $wName
		Wave m1 = $wName
		m1[0][0] = (wavemin(xW)) * xySize
		m1[1][0] = (wavemax(xW)) * xySize
		m1[0][1] = (W_coef[0] + (wavemin(xW) * W_coef[1])) * xySize
		m1[1][1] = (W_coef[0] + (wavemax(xW) * W_coef[1])) * xySize
		m1[][2] = nZ
	endif
	KillWaves fit_tempYw
End

// Orients vectors away from the nearest spindle pole
Function Polarise()
	String VectorList = WaveList("vec_*",";","")
	Variable nVectors = ItemsInList(VectorList)
	// In vector wave, row 0 and 1 are xy coords for points A and B, pick Z from name
	// Spindle poles are 1 and 2
	WAVE spWave
	Variable sp1x = spWave[0][0]
	Variable sp1y = spWave[0][1]
	Variable sp1z = spWave[0][2]
	Variable sp2x = spWave[1][0]
	Variable sp2y = spWave[1][1]
	Variable sp2z = spWave[1][2]
	Variable sp1_A,sp1_B,sp2_A,sp2_B
	Variable ABx, CDx, ABy, CDy
	String wName
	Variable nearest
	Make/O/N=(nVectors)/T pol_Name	// name of vector wave
	Make/O/N=(nVectors) pol_Des	// which spindle pole is it from
	Make/O/N=(nVectors) pol_Rev	// did the polarity get reversed?
	Make/O/N=(nVectors) pol_Angle // what is the angle relative to the spindle axis?
	Variable i
	
	for(i = 0; i < nVectors; i += 1)
		wName = StringFromList(i,VectorList)
		Wave w0 = $wName
		pol_Name[i] = wName
		sp1_A = sqrt((w0[0][0] - sp1X)^2 + (w0[0][1] - sp1Y)^2 + (w0[0][2] - sp1Z)^2)
		sp1_B = sqrt((w0[1][0] - sp1X)^2 + (w0[1][1] - sp1Y)^2 + (w0[1][2] - sp1Z)^2)
		sp2_A = sqrt((w0[0][0] - sp2X)^2 + (w0[0][1] - sp2Y)^2 + (w0[0][2] - sp2Z)^2)
		sp2_B = sqrt((w0[1][0] - sp2X)^2 + (w0[1][1] - sp2Y)^2 + (w0[1][2] - sp2Z)^2)
		nearest = min(sp1_A,sp1_B,sp2_A,sp2_B)
		if(nearest == sp1_A || nearest == sp1_B)
			pol_Des[i] = 1
			if(sp1_A >= sp1_B)
				Reverse/P/DIM=0 w0
				pol_Rev[i] = 1
			else
				pol_Rev[i] = 0
			endif
			// line AB is between spindle poles
			// line CD is the MT vector
			ABx = sp2X - sp1X
			CDx = w0[1][0] - w0[0][0]
			ABy = sp2Y - sp1Y
			CDy = w0[1][1] - w0[0][1]
			pol_Angle[i] = (atan2(ABy,ABx) - atan2(CDy,CDx)) * (180/pi)
		else
			pol_Des[i] = 2
			if(sp2_A >= sp2_B)
				Reverse/DIM=0 w0
				pol_Rev[i] = 1
			else
				pol_Rev[i] = 0
			endif
			ABx = sp1X - sp2X
			CDx = w0[1][0] - w0[0][0]
			ABy = sp1Y - sp2Y
			CDy = w0[1][1] - w0[0][1]
			pol_Angle[i] = (atan2(ABy,ABx) - atan2(CDy,CDx)) * (180/pi)
		endif
		
		if(pol_Angle[i] > 180)
			pol_Angle[i] -= 360
		elseif(pol_angle[i] < -180)
			pol_Angle[i] += 360
		endif
	endfor
End

Function MakeMaps()

	NVAR /Z xySize = gpxSize
	WAVE spWave,pol_Angle,pol_Des,segAngleWave,e_angleWave
	WAVE/T e_nameWave

	DoWindow/K allPlot // for back compatability
	DoWindow/K polePlot
	Display /N=polePlot
	DoWindow/K elliPlot
	Display /N=elliPlot
	
	String vecList = WaveList("vec_*",";","")
	String wName
	Variable nVec = ItemsInList(vecList)
	
	Variable i
	
	for(i = 0; i < nVec; i += 1)
		wName = StringFromList(i,vecList)
		AppendToGraph/W=polePlot $wName[][1] vs $wName[][0]
		AppendToGraph/W=elliPlot $wName[][1] vs $wName[][0]
	endfor
 
	vecList = TraceNameList("elliPlot",";",1)
	nVec = ItemsInList(VecList)	
	
	Make/O/N=(9,3) colorWave
	colorWave[][0] = {65535,65278,65021,65021,65021,61937,55769,42662,32639}
	colorWave[][1] = {62965,59110,53456,44718,36237,26985,18504,13878,10023}
	colorWave[][2] = {60395,52942,41634,27499,15420,4883,257,771,1028}
	Variable cW
	String elliName
	
	for(i = 0; i < nVec; i += 1)	
		wName = StringFromList(i,vecList)
		elliName = ReplaceString("vec",wName,"elli")
		FindValue/TEXT=elliName e_NameWave
			if (V_Value != -1)
				cW = floor(abs(e_angleWave[V_Value])/20)
				if (numtype(cW) == 0)
					ModifyGraph/W=elliPlot rgb($wName)=(colorWave[cW][0],colorWave[cW][1],colorWave[cW][2])
				endif
			else
				RemoveFromGraph/W=elliPlot $wName
			endif
	endfor
	
	// format polePlot
	DoWindow/F polePlot
	ModifyGraph /W=polePlot rgb=(32896,32896,32896)
	AppendToGraph/W=polePlot spWave[][1] vs spWave[][0]
	ModifyGraph /W=polePlot rgb(spWave)=(65535,0,0)
	ModifyGraph/W=polePlot width={Plan,1,bottom,left}
	SetAxis/W=polePlot/R left 768*xysize,0
	SetAxis/W=polePlot bottom 0,768*xysize
	ModifyGraph mirror=1,noLabel=2,axRGB=(34952,34952,34952)
	ModifyGraph tlblRGB=(34952,34952,34952),alblRGB=(34952,34952,34952)
	ModifyGraph margin=14
	SavePICT/WIN=polePlot/E=-5/RES=300/TRAN=1/W=(0,0,392,392) as "Clipboard"
	LoadPICT/O/Q "Clipboard", polePic
	KillWindow/Z polePlot
	
	// format elliPlot
	DoWindow/F elliPlot
	ModifyGraph/W=elliPlot gbRGB=(32896,32896,32896)
	ModifyGraph/W=elliPlot width={Plan,1,bottom,left}
	SetAxis/W=elliPlot/R left 768*xysize,0
	SetAxis/W=elliPlot bottom 0,768*xysize
	ModifyGraph mirror=1,noLabel=2,axRGB=(34952,34952,34952)
	ModifyGraph tlblRGB=(34952,34952,34952),alblRGB=(34952,34952,34952)
	ModifyGraph margin=14
	SavePICT/WIN=elliPlot/E=-5/RES=300/TRAN=1/W=(0,0,392,392) as "Clipboard"
	LoadPICT/O/Q "Clipboard", elliPic
	
	vecList = TraceNameList("elliPlot",";",1)
	nVec = ItemsInList(VecList)	
	
	for(i = 0; i < nVec; i += 1)	
		wName = StringFromList(i,vecList)
		elliName = ReplaceString("vec",wName,"elli")
		FindValue/TEXT=elliName e_NameWave
			if (e_angleWave[V_Value] < 20)
				RemoveFromGraph/W=elliPlot $wName
			endif
	endfor
	
	SavePICT/WIN=elliPlot/E=-5/RES=300/TRAN=1/W=(0,0,392,392) as "Clipboard"
	LoadPICT/O/Q "Clipboard", wonkPic
	KillWindow/Z elliPlot
End

// Creates PDF report of all the analysis
Function TidyAndReport()
	NVAR /Z xySize = gpxSize
	WAVE spWave,pol_Angle,pol_Des,segAngleWave,e_angleWave
	SVAR expCond = TIFFtitle
 
	MakeMaps()
	
	// leave histlist like this in the case where code is run on a very old pxp
	String histList = "sp1Hist;sp2Hist;allHist;allposHist;segAngleHist;segposHist;elliHist;"
	String histName
	Variable i
	
	for(i = 0; i < ItemsInList(histList); i += 1)
		histName = StringFromList(i,histList)
		DoWindow/K $histName
	endfor
	
	Duplicate/O pol_Angle pol_Angle_1,pol_Angle_2
	pol_Angle_1 = (pol_Des == 1) ? pol_Angle_1 : NaN
	pol_Angle_2 = (pol_Des == 2) ? pol_Angle_2 : NaN
	WaveTransform zapnans pol_Angle_1
	WaveTransform zapnans pol_Angle_2
	Concatenate/O {pol_Angle_1,pol_Angle_2}, pol_Angle_all
	Duplicate/O pol_Angle_all pol_Angle_all_pos
	pol_Angle_all_pos = abs(pol_Angle_all[p])
	Make/N=180/O pol_Angle_all_pos_Hist
	Histogram/B={0,2,90} pol_Angle_all_pos,pol_Angle_all_pos_Hist
	Display/N=allposHist pol_Angle_all_pos_Hist
	ModifyGraph/W=allposHist rgb=(32767,32767,32767)
	TextBox/C/N=text0/F=0/A=RT/X=0.00/Y=0.00 "Spindle axis"
	
	Duplicate/O segAngleWave segAngleWave_all
	Duplicate/O segAngleWave_all segAngleWave_all_pos
	segAngleWave_all_pos = abs(segAngleWave_all[p])
	Make/N=90/O seg_angle_pos_Hist
	Histogram/B={0,2,90} segAngleWave_all_pos,seg_angle_pos_Hist
	Display/N=segposHist seg_angle_pos_Hist
	ModifyGraph/W=segposHist rgb=(32768,32770,65535)
	TextBox/C/N=text0/F=0/A=RT/X=0.00/Y=0.00 "Nearest MT segments"
	
	Make/N=90/O e_angleWave_Hist
	Histogram/B={0,2,90} e_angleWave,e_angleWave_Hist
	Display/N=elliHist e_angleWave_Hist
	ModifyGraph/W=elliHist rgb=(65535,43688,32768)
	TextBox/C/N=text0/F=0/A=RT/X=0.00/Y=0.00 "Ellipse comparison"
	
	DoWindow /K summaryLayout
	NewLayout /N=summaryLayout
	AppendLayoutObject /W=summaryLayout picture polePic
	AppendLayoutObject /W=summaryLayout picture elliPic
	AppendLayoutObject /W=summaryLayout picture wonkPic
	
	histlist = "allposHist;segposHist;elliHist;"
	
	for(i = 0; i < ItemsInList(histList); i += 1)
		histName = StringFromList(i,histList)
		Label/W=$histName bottom "Relative angle (°)"
		Label/W=$histName left "Frequency"
		ModifyGraph/W=$histName mode=5,hbFill=4
		SetAxis/W=$histName/A/N=1/E=1 left
		AppendLayoutObject /W=summaryLayout graph $histName
	endfor

	// Tidy report
	DoWindow /F summaryLayout
	// in case these are not captured as prefs
#if igorversion()>=7
	LayoutPageAction size(-1)=(595, 842), margins(-1)=(18, 18, 18, 18)
#endif
	ModifyLayout units=0
	ModifyLayout frame=0,trans=1
	ModifyLayout left(allposHist)=300,top(allposHist)=21,width(allposHist)=180,height(allposHist)=130
	ModifyLayout left(segposHist)=300,top(segposHist)=171,width(segposHist)=180,height(segposHist)=130
	ModifyLayout left(elliHist)=300,top(elliHist)=321,width(elliHist)=180,height(elliHist)=130
	TextBox/C/N=text0/F=0/A=RB/X=0.00/Y=0.00 expCond
	ModifyLayout left(polePic)=21,top(polePic)=21,width(polePic)=250,height(polePic)=250
	ModifyLayout left(elliPic)=21,top(elliPic)=285,width(elliPic)=250,height(elliPic)=250
	ModifyLayout left(wonkPic)=21,top(wonkPic)=551,width(wonkPic)=250,height(wonkPic)=250
	SavePICT/E=-2 as expCond + ".pdf"
End

// Analysis of short MT segments within 120 nm of one another
Function segWrapper()
	NVAR/Z nZ = fileIndex
	NVAR/Z zSize = gzSize
	if (!NVAR_Exists(zSize))
		Variable/G gzSize = 60
	endif
	
	String mList = WaveList("vec_*",";","")
	String matList = mList
	Variable nVec = ItemsInList(mList)
	Make/O/T/N=(nVec) segLabelWave=""
	Make/O/N=(nVec) segLengthWave=NaN
	Make/O/T/N=(nVec*500) seg1Wave="",seg2Wave=""
	Make/O/N=(nVec*500) segDistWave=NaN,segAngleWave=NaN
	String mName,subList,negList,sliceList
	Variable tempVar
	String mName0,mName1
	
	Variable i,j,k,l=0
	
	// exclude short MT segments on the basis of length (60 nm)
	for(i = 0; i < nVec; i += 1)
		mName = StringFromList(i,mList)
		Wave m0 = $mName
		segLabelWave[i] = mName
		MatrixOp/FREE tempmatP = row(m0,0)
		MatrixOp/FREE tempmatQ = row(m0,1)
		MatrixOP/FREE tempMat = tempmatP - tempmatQ
		tempVar = norm(tempMat)
		segLengthWave[i] = tempvar
		if(tempvar <= 60)
			matList = RemoveFromList(mName, matList)
		elseif(numtype(tempvar)==2)
			matList = RemoveFromList(mName, matList)
		endif
	endfor
	
	for(i = 0; i < nZ; i += zSize)
		subList = WaveList("vec_" + num2str(i) + "*",";","")
		if(ItemsInList(subList) > 1)
			negList = matList
			sliceList = matList
			negList = RemoveFromList(subList,negList)	// remove subList from matList copy, making negative list
			sliceList = RemoveFromList(negList,sliceList)	//	remove the negative list from matList copy leaving subList >= 60 nm long
			nVec = ItemsInList(sliceList)
			if(nVec > 1)
				for(j = 0; j < nVec; j += 1)
					mName0 = StringFromList(j,sliceList)
					Wave m0 = $mName0
					for(k = 0; k < nVec; k += 1)
						if(j > k)
							mName1 = StringFromList(k,sliceList)
							Wave m1 = $mName1
							seg1Wave[l] = mName0
							seg2Wave[l] = mName1
							segDistWave[l] = seg2seg(m0,m1)
							if(segDistWave[l] < 120)	// neighbour segments less than 120 nm apart are analysed
								segAngleWave[l] = ssAngle(m0,m1)
							else
								segAngleWave[l] = NaN
							endif
							l += 1
						endif
					endfor
				endfor
			endif
		endif
	endfor
	// trim seg* waves, can't zapnans
	nVec = numpnts(seg1Wave) // reuse variable
	DeletePoints l, nVec - l, segLabelWave,segLengthWave,seg1Wave,seg2Wave,segDistWave,segAngleWave
End

// Function to find the distance between two MT segments (at closest approach)
///	@param	m0		matrix wave containing 2D coords for segment 1
///	@param	m1		matrix wave containing 2D coords for segment 2
Function seg2seg(m0,m1)
	Wave m0,m1
	MatrixOp/O matP = row(m0,0)
	MatrixOp/O matQ = row(m0,1)
	MatrixOp/O matR = row(m1,0)
	MatrixOp/O matS = row(m1,1)
	
	Variable vSmall = 0.00001
	
	MatrixOP/O mat_u = matQ - matP
   MatrixOP/O mat_v = matS - matR
   MatrixOP/O mat_w = matP - matR
   Variable aa = MatrixDot(mat_u,mat_u) // always >= 0
   Variable bb = MatrixDot(mat_u,mat_v)
   Variable cc = MatrixDot(mat_v,mat_v) // always >= 0
   Variable dd = MatrixDot(mat_u,mat_w)
   Variable ee = MatrixDot(mat_v,mat_w)
   Variable bigD = aa*cc - bb*bb	//always >= 0
   Variable sc, sN, sD = bigD 	// sc = sN / sD, default sD = D >= 0
   Variable tc, tN, tD = bigD	// tc = tN / tD, default tD = D >= 0
	// compute the line parameters of the two closest points
	if (bigD < vSmall)  // the lines are almost parallel
		sN = 0         // force using point P0 on segment S1
		sD = 1        // to prevent possible division by 0.0 later
		tN = ee
		tD = cc
    else                 // get the closest points on the infinite lines
		sN = (bb*ee - cc*dd)
		tN = (aa*ee - bb*dd)
		if (sN < 0)       // sc < 0 => the s=0 edge is visible
			sN = 0
			tN = ee
			tD = cc
		elseif (sN > sD) // sc > 1  => the s=1 edge is visible
			sN = sD
			tN = ee + bb
			tD = cc
		endif
	endif

	if (tN < 0)            // tc < 0 => the t=0 edge is visible
		tN = 0
		// recompute sc for this edge
		if (-dd < 0)
			sN = 0
		elseif (-dd > aa)
			sN = sD
		else
			sN = -dd
			sD = aa
		endif	
	elseif (tN > tD)      // tc > 1  => the t=1 edge is visible
		tN = tD
		// recompute sc for this edge
		if ((-dd + bb) < 0)
			sN = 0
		elseif ((-dd + bb) > aa)
			sN = sD
		else
			sN = (-dd +  bb)
			sD = aa
		endif
    endif
	// finally do the division to get sc and tc
	sc = (abs(sN) < vSmall ? 0 : sN / sD)
	tc = (abs(tN) < vSmall ? 0 : tN / tD)
	// get the difference of the two closest points
	MatrixOp/O matdP = mat_w + (sc * mat_u) - (tc * mat_v);  // =  S1(sc) - S2(tc)
	Return norm(matdP)   // return the closest distance
End

// Function to find the angle between two MT segments
///	@param	m0		matrix wave containing 2D coords for segment 1
///	@param	m1		matrix wave containing 2D coords for segment 2
Function ssAngle(m0,m1)
	Wave m0,m1
	
	Variable PQx, RSx, PQy, RSy, angle
	
	PQx = m0[1][0] - m0[0][0]
	RSx = m1[1][0] - m1[0][0]
	PQy = m0[1][1] - m0[0][1]
	RSy = m1[1][1] - m1[0][1]
	angle = (atan2(PQy,PQx) - atan2(RSy,RSx)) * (180/pi)
	if(angle > 180)
		angle -= 360
	elseif(angle < -180)
		angle += 360
	endif	
	Return angle
End

// Function to compare midpoint of MT segment to ellipsoid tangent
Function elliWrapper()
	// first get list of eligible vectors
	WAVE/Z segLengthWave
	WAVE/T/Z segLabelWave
	Variable nWaves = numpnts(segLengthWave)
	String mName, matList = ""
	Variable i
	
	for(i = 0; i < nWaves; i += 1)
		if(segLengthWave[i] > 60)
			matList = matList + segLabelWave[i] + ";"
		endif
	endfor
	
	WAVE spWave
	// find spindle midpoint, c
	Variable cx = (spWave[0][0] + spWave[1][0]) / 2
	Variable cy = (spWave[0][1] + spWave[1][1]) / 2
	Variable cz = (spWave[0][2] + spWave[1][2]) / 2
	
	// centre spindle axis
	Duplicate/O spWave, e_spWave
	e_spWave[][0] -= cx
	e_spWave[][1] -= cy
	e_spWave[][2] -= cz
	// find theta and phi for e_spwave
	Variable wx = e_spWave[0][0] - 0
	Variable wy = e_spWave[0][1] - 0
	Variable wz = e_spWave[0][2] - 0
	// inclination/polar, theta. azimuth, phi
	Variable theta = acos(wz / (sqrt( (wx^2) + (wy^2) + (wz^2) ) ) )
	Variable phi = atan2(wy,wx)
	// rotate spindle axis
	Make/O zRotationMatrix={{cos(phi),sin(phi),0},{-sin(phi),cos(phi),0},{0,0,1}}
	Make/O yRotationMatrix={{cos(theta),0,-sin(theta)},{0,1,0},{sin(theta),0,cos(theta)}}
	MatrixMultiply e_spWave, zRotationMatrix
	Wave M_Product
	MatrixMultiply M_Product, yRotationMatrix
	Duplicate/O M_Product r_spWave
	// determine length c (point c to point p1) 
	Variable cc = sqrt(wx^2 + wy^2 + wz^2)
	
	// loop through all MT vectors
	Variable nVec = ItemsInList(matList)
	String newName
	Variable zt
	Make/O/N=(nVec,3) e_mpWave,e_avWave,e_prWave // midpoint, actual vector, proposed vector
	Make/O/N=(nVec) e_rWave,e_angleWave
	Make/O/N=(nVec)/T e_nameWave
	Variable rr // length of vector for normalisation
	
	for(i = 0; i < nVec; i += 1)
		mName = StringFromList(i,matList)
		Wave m0 = $mName
		newName = ReplaceString("vec_",mName,"elli_")
		Duplicate/O m0, $newName
		Wave m1 = $newName
		// subtract c from all points
		m1[][0] -= cx
		m1[][1] -= cy
		m1[][2] -= cz
		// rotate all points by theta and phi
		MatrixMultiply m1, zRotationMatrix
		MatrixMultiply M_Product, yRotationMatrix
		Duplicate/O M_Product $newname
		e_nameWave[i] = newName
		// find midpoint
		wx = (m1[0][0] + m1[1][0]) / 2
		wy = (m1[0][1] + m1[1][1]) / 2
		wz = (m1[0][2] + m1[1][2]) / 2
		Make/O/FREE/N=(1,3) mpWave = {{wx},{wy},{wz}}
		e_mpWave[i][] = mpWave[0][q]
		// removing this exclusion criteria // if(norm(mpWave) < cc)
		// transform z coord for mhat(x)
		zt = (wz^2 - cc^2) / wz
		// make actual vector
		MatrixOp/O/FREE avWave = row(m1,1)
		avWave[0][] -= mpWave[0][q]
		rr = norm(avWave)
		avWave /= rr // normalise
		e_avWave[i][] = avWave[0][q]
		e_rWave[i] = rr
		// make proposed endpoint then vector
		Make/O/FREE/N=(1,3) prWave = {{wx},{wy},{zt}}
		rr = norm(prWave)
		prWave /= rr
		e_prWave[i][] = prWave[0][q]
	endfor
	
	PutEllipseBack(matList,cx,cy,cz)
	
	WAVE/Z vec3D,elli3Dre
	nVec = dimsize(vec3D,0)
	Make/O/N=(nVec/3,3) matvA,matvB,mateA,mateB
	Variable j=0
	
	for(i = 0; i < nVec/3; i += 1)
		matvA[i][] = vec3D[j][q]
		matvB[i][] = vec3D[j+1][q]
		mateA[i][] = elli3Dre[j][q]
		mateB[i][] = elli3Dre[j+1][q]
		j += 3
	endfor
	
	// subtract to get vectors
	matvB[][] -= matvA[p][q]
	mateB[][] -= mateA[p][q]
	// project onto z = 0
	matvB[][2] = 0
	mateB[][2] = 0
	
	nVec = dimsize(matvB,0)
	Variable tempvar
	
	for(i = 0; i < nVec; i += 1)
		MatrixOp/O/FREE avWave = row(matvB,i)
		MatrixOp/O/FREE prWave = row(mateB,i)
		MatrixOp/O/FREE interMat = avWave . prWave
		tempvar = norm(avWave) * norm(prWave)
		interMat /=tempvar
		e_angleWave[i] = acos(interMat[0])
	endfor
	e_angleWave *= (180 / pi)
End

// Function to lay the spindle back down so that projection can be done
///	@param	eList	string with wavelist of eligible vec_ waves
///	@param	cx		coords for c, the midpoint of p1 and p2
///	@param	cy		coords for c, the midpoint of p1 and p2
///	@param	cz		coords for c, the midpoint of p1 and p2
Function PutEllipseBack(matList,cx,cy,cz)
	String matList
	Variable cx,cy,cz
	
	String elliList = ReplaceString("vec_",matList,"elli_")
	String vecList = ReplaceString("elli_",elliList,"vec_")
	Concatenate/O/NP=0 elliList, elli3D
	Concatenate/O/NP=0 vecList, vec3D
	Variable nRows = DimSize(vec3D,0)
	
	Variable i,j=0

	for(i = 2; i < (nRows/2) * 3; i += 3)
		InsertPoints i, 1, vec3D,elli3D
		elli3D[i][] = NaN
		vec3D[i][] = NaN
	endfor
	
	WAVE e_avWave,e_rWave,e_mpWave,e_prWave
	
	Duplicate/O e_avWave, e_avWave2
	e_avWave2 *= e_rWave[p]
	Duplicate/O/FREE e_avWave2, e_avWave21
	Duplicate/O e_avWave2, e_avWaveEP
	e_avWaveEP += e_mpWave[p][q] // new endpoint
	Duplicate/O e_mpWave, e_avWaveSP
	e_avWaveSP -= e_avWave21[p][q] // new startpoint
	
	Duplicate/O e_prWave, e_prWave2
	e_prWave2 *= e_rWave[p]
	Duplicate/O/FREE e_prWave2, e_prWave21
	Duplicate/O e_prWave2, e_prWaveEP
	e_prWaveEP += e_mpWave[p][q] // new endpoint
	Duplicate/O e_mpWave, e_prWaveSP
	e_prWaveSP -= e_prWave21[p][q] // new startpoint

	nRows = DimSize(e_avWave,0)
	Make/O/N=(nRows*3,3) vec3Dre,elli3Dre
	
	for(i = 0; i < nRows *3; i +=3)
		vec3Dre[i][] = e_avWaveSP[j][q]
		vec3Dre[i+1][] = e_avWaveEP[j][q]
		vec3Dre[i+2][] = NaN
		
		elli3Dre[i][] = e_prWaveSP[j][q]
		elli3Dre[i+1][] = e_prWaveEP[j][q]
		elli3Dre[i+2][] = NaN
		j += 1
	endfor
	
	Wave zRotationMatrix,yRotationMatrix
	
	Duplicate/O zRotationMatrix, zBackMatrix
	Duplicate/O yRotationMatrix, yBackMatrix
	MatrixTranspose zBackMatrix
	MatrixTranspose yBackMatrix
	
	MatrixMultiply elli3Dre, yBackMatrix
	Wave M_product
	MatrixMultiply M_product, zBackMatrix
	Duplicate/O M_Product, elli3Dre
	
	elli3Dre[][0] += cx
	elli3Dre[][1] += cy
	elli3Dre[][2] += cz
	
	KillWaves vec3Dre,elli3D,e_avWave2,e_prWave2
End
back to top