#pragma rtGlobals=1 // Use modern global access method. #pragma version = 1.4 // #include "vector-math" // was needed for normalize() Constant debugN = 0 Menu "Grains" "(Info" "Statistics of a Boundary",BoundaryStats($"",NaN,NaN,NaN) "Check for All Sigma Boundarys",checkAllSigmaBoundarys($"",NaN,NaN,NaN) "List Neighbors to a Grain",makeBorderList($"",NaN) "Angle Between two Grains",angleBetweenGrains($"",NaN,NaN) " Angle Between two Voxels",AskAngleBetweenPointsSymReduced(NaN,NaN) "Find Number of Grains Above a Size", NumOfGrainsBiggerThan($"",NaN) "-" "Table of grain centers & orientations",Table_GrainOrientations($"") "Table of a multigrain 3d wave",Table_AllGrains($"") "Graph of the grain volumes", Graph_grain_Vol($"") "Table of XYZ indicies",Table_XYZindex($"") "Table of all grain statistics",Table_GrainStatistics($"") "Histogram of total angles",Graph_HistTotalAngle($"") "-" "(New Data" "Prepare\Read In data",PrepareData() "Calculate Grains from Neighbor Rotations",findGrainsByComparingNeighbors(NaN,NaN,0) End Menu "Load Waves" "-" "(Gizmo Grain Data" "Prepare\Read In data",PrepareData() "Calculate Grains from Neighbor Rotations",findGrainsByComparingNeighbors(NaN,NaN,0) End //Menu "Grains" // "Zoomer Panel for a Gizmo",PanelGizmoView() // "-" //End // when putting up a sphere, subtract (1,1,1) from it so that it matches the iso-surfaces // e.g. if I want a sphere at (2,3,4), put it at (1,2,3), see MoveGizmoCursorProc() for example // angle ~ sqrt(axisX^2+axisY^2+axisZ^2)*180/PI/2 * 1.04386 Window PanelGizmoView() : Panel PauseUpdate; Silent 1 // building window... String gizWin = StringFromList(0,WinList("*",";","WIN:4096")) // gizmo window for positioning if (strlen(WinList("Panel_GizmoViewer","","WIN:64"))>1) DoWindow/F Panel_GizmoViewer AutoPositionWindow /M=0/R=$gizWin Panel_GizmoViewer return endif initGrainGizmo() NewPanel /K=1 /W=(690,50,859,620) // W=(left, top, right, bottom ) DoWindow /C Panel_GizmoViewer AutoPositionWindow /M=0/R=$gizWin Panel_GizmoViewer // position panel by top most gizmo SetDrawLayer UserBack SetDrawEnv xcoord= rel,ycoord= abs,linethick= 3 DrawLine 0,292,1,292 SetDrawEnv xcoord= rel,ycoord= abs,linethick= 3 DrawLine 0,443,1,443 SetDrawEnv xcoord= rel,ycoord= abs,linethick= 3 DrawLine 0,536,1,536 String micron = StrVarOrDefault("root:Packages:Grains:micron","µm") Button buttonHome,pos={22,99},size={50,20},proc=ButtonHomeProc,title="Home" Button buttonStopRotation,pos={85,99},size={50,20},proc=ButtonStopProc,title="Stop" Button buttonStopRotation help={"stp rotating"} Button buttonZoomIn,pos={10,3},size={65,20},proc=ButtonZoomProc,title="Zoom In" Button buttonZoomOut,pos={85,3},size={75,20},proc=ButtonZoomProc,title="Zoom Out" Button buttonXplus,pos={43,27},size={30,20},proc=ButtonTranslateProc,title="X+" Button buttonXminus,pos={83,27},size={30,20},proc=ButtonTranslateProc,title="X-" Button buttonYplus,pos={43,51},size={30,20},proc=ButtonTranslateProc,title="Y+" Button buttonYminus,pos={83,51},size={30,20},proc=ButtonTranslateProc,title="Y-" Button buttonZplus,pos={43,75},size={30,20},proc=ButtonTranslateProc,title="Z+" Button buttonZminus,pos={83,75},size={30,20},proc=ButtonTranslateProc,title="Z-" PopupMenu popupGrainDim,pos={34,123},size={100,20},proc=PopGrainProc,title="dim grain" PopupMenu popupGrainDim,mode=1,popvalue="none",value= #"\"none;\"+getGrainNameList()" PopupMenu popupGrainBrite,pos={22,147},size={111,20},proc=PopGrainProc,title="briten grain" PopupMenu popupGrainBrite,mode=1,popvalue="none",value= #"\"none;\"+getGrainNameList()" PopupMenu popupGrainRestore,pos={16,171},size={118,20},proc=PopGrainProc,title="restore grain" PopupMenu popupGrainRestore,mode=1,popvalue="none",value= #"\"none;\"+getGrainNameList()" Button buttonAllDim,pos={40,195},size={85,20},proc=ButtonGrainColorProc,title="Dim All" Button buttonAllSolid,pos={40,219},size={85,20},proc=ButtonGrainColorProc,title="All Solid" Button buttonAllRestore,pos={40,243},size={85,20},proc=ButtonGrainColorProc,title="Restore All" PopupMenu GizmoPopup,pos={48,267},size={64,20},proc=PopGizmoProc,fSize=12 PopupMenu GizmoPopup,mode=1,popvalue="Gizmo",value= #"\"Gizmo;Show Axes;Hide Axes;Re-Compile;Show Info Window\"" PopupMenu popupBndryGrain1,pos={19,299},size={87,20},proc=PopGrainBoundryProc,title="grain1" PopupMenu popupBndryGrain1,help={"select first grain of boundary, usually central grain"} PopupMenu popupBndryGrain1,mode=1,popvalue="none",value= #"\"none;\"+getGrainNameList()" PopupMenu popupBndryGrain2,pos={19,322},size={87,20},proc=PopGrainBoundryProc,title="grain2" PopupMenu popupBndryGrain2,help={"select secpmd grain of boundary"} PopupMenu popupBndryGrain2,mode=1,popvalue="none",value= #"\"none;\"+getGrainNameList()" SetVariable setRadius,pos={10,346},size={140,18},title="radius ("+micron+")",fSize=12,format="%.2f" SetVariable setRadius,limits={0,inf,.5},value= root:Packages:Grains:radiusBndry SetVariable setRadius,help={"distance from cursor to accept when finding grain boundary points ("+micron+")"} Button buttonCalcBndry,pos={11,367},size={136,20},proc=ButtonGrainBndryProc,title="Update Boundary" Button buttonCalcBndry,help={"calculate the grain boundary parameters"} Button buttonCenterBndry,pos={11,392},size={136,20},proc=ButtonGrainBndryProc,title="Center on Boundary" Button buttonCenterBndry,help={"re-center plot on the grain boundary"} PopupMenu popupScatterBndry,pos={10,417},size={143,20},proc=PopGrainBoundryPointsProc,title="boundary points" PopupMenu popupScatterBndry,help={"turns scatter boundary points on/off"} PopupMenu popupScatterBndry,fSize=12,mode=1,popvalue="Off",value= #"\"Off;On\"" SetVariable Xcursor,pos={1,452},size={70,18},proc=MoveGizmoCursorProc,title="X" SetVariable Xcursor,fSize=12 SetVariable Xcursor,limits={-10,inf,1},value= root:Packages:Grains:Xcursor SetVariable Ycursor,pos={1,472},size={70,18},proc=MoveGizmoCursorProc,title="Y" SetVariable Ycursor,fSize=12 SetVariable Ycursor,limits={-10,inf,1},value= root:Packages:Grains:Ycursor SetVariable Zcursor,pos={1,492},size={70,18},proc=MoveGizmoCursorProc,title="Z" SetVariable Zcursor,fSize=12 SetVariable Zcursor,limits={-10,inf,1},value= root:Packages:Grains:Zcursor Button CursorOn,pos={75,459},size={90,20},proc=ButtonGizmoCursorProc,title="Cursor ON" Button CursorOff,pos={75,482},size={90,20},proc=ButtonGizmoCursorProc,title="Cursor OFF" Button GetGrainNum,pos={11,513},size={90,20},proc=ButtonGizmoCursorProc,title="Get Grain #" SetVariable grainNumber,pos={108,513},size={50,18},proc=SetGrainNumProc,title=" " SetVariable grainNumber,help={"grain number works with 3d cursor"},fSize=12 SetVariable grainNumber,fStyle=1 SetVariable grainNumber,limits={0,inf,1},value= root:Packages:Grains:cursorGrainNum SetVariable explode,pos={19,544},size={122,18},proc=ExplodeProc,title="Explode" SetVariable explode,help={"set exploded view level [0,100]"},fSize=12,fStyle=1 SetVariable explode,limits={0,100,1},value= root:Packages:Grains:explode EndMacro // These two fucntions are needed to control the cursor marker on the gizmo plot Function MoveGizmoCursorProc(ctrlName,varNum,varStr,varName) : SetVariableControl String ctrlName Variable varNum String varStr String varName NVAR xx = root:Packages:Grains:Xcursor NVAR yy = root:Packages:Grains:Ycursor NVAR zz = root:Packages:Grains:Zcursor String cmd sprintf cmd, "ModifyGizmo opName=translateCursor, operation=translate, data={%g,%g,%g}",xx,yy,zz Execute cmd sprintf cmd, "ModifyGizmo opName=translateCursorBack, operation=translate, data={%g,%g,%g}",-xx,-yy,-zz Execute cmd // this next section makes sure that the range of the control is set to a bit larger than the size of the grains if (cmpstr(ctrlName,"Xcursor") || cmpstr(ctrlName,"Ycursor") || cmpstr(ctrlName,"Zcursor")) String gWaveName=StringFromList(0,getGrainWaveList()) // name of first grain wave in gizmo gWaveName = StringByKey("grainsWave", note($gWaveName),"=") if (exists(gWaveName)!=1) DoAlert 0, "unable to find 3d grains wave in ButtonGizmoCursorProc()" return 1 endif Wave grains=$gWaveName Variable xlo,xhi, ylo,yhi, zlo,zhi xlo = DimOffset(grains,0) ylo = DimOffset(grains,1) zlo = DimOffset(grains,2) xhi = xlo + DimDelta(grains,0)*DimSize(grains,0) yhi = ylo + DimDelta(grains,1)*DimSize(grains,1) zhi = zlo + DimDelta(grains,2)*DimSize(grains,2) SetVariable Xcursor,limits={xlo-2,xhi+2,1} SetVariable Ycursor,limits={ylo-2,yhi+2,1} SetVariable Zcursor,limits={zlo-2,zhi+2,1} endif End //Function MoveGizmoCursorProc(ctrlName,varNum,varStr,varName) : SetVariableControl // String ctrlName // Variable varNum // String varStr // String varName // NVAR xx = root:Packages:Grains:Xcursor // NVAR yy = root:Packages:Grains:Ycursor // NVAR zz = root:Packages:Grains:Zcursor // String cmd // sprintf cmd, "ModifyGizmo opName=translateCursor, operation=translate, data={%g,%g,%g}",xx,yy,zz // Execute cmd // sprintf cmd, "ModifyGizmo opName=translateCursorBack, operation=translate, data={%g,%g,%g}",-xx,-yy,-zz // Execute cmd //End Function ButtonGizmoCursorProc(ctrlName) : ButtonControl String ctrlName Execute "GetGizmo gizmoName" // check if a gizmo is up SVAR S_GizmoName=S_GizmoName if (strlen(S_GizmoName)<1) DoAlert 0, "No Gizmo Windows Open" return 1 endif if (stringmatch(ctrlName,"CursorOn")) Execute "ModifyGizmo modifyObject=sphereCursor, property={radius,0.7}" return 0 elseif (stringmatch(ctrlName,"CursorOff")) Execute "ModifyGizmo modifyObject=sphereCursor, property={radius,0.0}" return 0 elseif (stringmatch(ctrlName,"GetGrainNum")) // take the 3d cursor position, and update the grain number in the panel String gWaveName=StringFromList(0,getGrainWaveList()) // name of first grain wave in gizmo gWaveName = StringByKey("grainsWave", note($gWaveName),"=") if (exists(gWaveName)!=1) DoAlert 0, "unable to find 3d grains wave in ButtonGizmoCursorProc()" return 1 endif Wave grains=$gWaveName NVAR xx = root:Packages:Grains:Xcursor NVAR yy = root:Packages:Grains:Ycursor NVAR zz = root:Packages:Grains:Zcursor if (!NVAR_Exists(xx) || !NVAR_Exists(yy) || !NVAR_Exists(zz)) DoAlert 0, "unable to retrieve cursor positions ButtonGizmoCursorProc()" return 1 endif Variable grainNum = xyzPositionToGrainNum(grains,xx,yy,zz) if (exists("root:Packages:Grains:cursorGrainNum")==2) NVAR cursorGrainNum = root:Packages:Grains:cursorGrainNum cursorGrainNum = grainNum endif return 0 endif DoAlert 0, "ERROR, ButtonGizmoCursorProc() called with ctrlName= '"+ctrlName+"'" return 1 End //Function ButtonGizmoCursorProc(ctrlName) : ButtonControl // String ctrlName // if (stringmatch(ctrlName,"CursorOn")) // Execute "ModifyGizmo modifyObject=sphereCursor, property={radius,0.7}" // elseif (stringmatch(ctrlName,"CursorOff")) // Execute "ModifyGizmo modifyObject=sphereCursor, property={radius,0.0}" // else // DoAlert 0, "ERROR, ButtonGizmoCursorProc() called with ctrlName= '"+ctrlName+"'" // endif //End // take the 3d position (µm), and return the grain number Function xyzPositionToGrainNum(grains,xx,yy,zz) Wave grains // 3d wave with all of the grain numbers (e.g. gNeighbor) Variable xx,yy,zz if (!WaveExists(grains) || numtype(xx+yy+zz)) String grainName=NameOfWave(grains) Prompt grainName,"3d wave with all grains",popup,ListMultiGrainMats() xx = numtype(xx) ? 0 : xx yy = numtype(yy) ? 0 : yy zz = numtype(zz) ? 0 : zz String micron = StrVarOrDefault("root:Packages:Grains:micron","micron") Prompt xx,"X position in grain ("+micron+")" Prompt yy,"Y position in grain ("+micron+")" Prompt zz,"Z position in grain ("+micron+")" DoPrompt "choose grains",grainName,xx,yy,zz if (V_flag) return -1 endif Wave grains = $grainName endif if (!WaveExists(grains)) return -1 endif Variable i,j,k,gNum=NaN i = (xx-DimOffset(grains,0))/DimDelta(grains,0) j = (yy-DimOffset(grains,1))/DimDelta(grains,1) k = (zz-DimOffset(grains,2))/DimDelta(grains,2) if (numtype(i+j+k)==0 && i>=0 && j>=0 && k>=0 && i initGrainGizmo()" return 1 endif String gWaveName=StringFromList(0,getGrainWaveList()) // name of first grain wave in gizmo gWaveName = StringByKey("grainsWave", note($gWaveName),"=") if (exists(gWaveName)!=1) DoAlert 0, "unable to find 3d grains wave in SetGrainNumProc()" return 1 endif Wave grains=$gWaveName // finally this is the 3d wave of all grains Wave com=$(GetWavesDataFolder(grains,2)+"_COM") // wave with single index to com of each grain Variable index = com[grainNum] // single index to com of grainNum if (numtype(index)) DoAlert 0, "got back a point index of "+""+"" endif String dataPath = GetWavesDataFolder(grains,1)+"raw:" Wave XX=$(dataPath+"XX") // x,y,z positions read in from file Wave YY=$(dataPath+"YY") Wave ZZ=$(dataPath+"ZZ") if (!WaveExists(XX) || !WaveExists(YY) || !WaveExists(ZZ)) DoAlert 0, "unable to find XX, YY, ZZ waves in SetGrainNumProc()" return 1 endif NVAR Xcursor = root:Packages:Grains:Xcursor NVAR Ycursor = root:Packages:Grains:Ycursor NVAR Zcursor = root:Packages:Grains:Zcursor Xcursor = XX[index] // set cursor xyz to com of grainNum Ycursor = YY[index] Zcursor = ZZ[index] MoveGizmoCursorProc("SetGrainNumProc",0,"","") // move the cursor to this position ButtonGizmoCursorProc("CursorOn") // make sure cursor is on return 0 End Function PopGrainBoundryPointsProc(ctrlName,popNum,popStr) : PopupMenuControl String ctrlName Variable popNum String popStr if (!stringmatch(ctrlName,"popupScatterBndry")) return 1 endif String cmd sprintf cmd, "ModifyGizmo ModifyObject=scatterBndry property={ size,%d}",(stringmatch(popStr,"On") Execute cmd End Function PopGizmoProc(ctrlName,popNum,popStr) : PopupMenuControl String ctrlName Variable popNum String popStr if (stringmatch(popStr,"Show Axes")) // Execute "ModifyGizmo showAxisCue=1" Execute "ModifyGizmo insertDisplayList=2, object=freeAxesCue0" elseif (stringmatch(popStr,"Hide Axes")) Execute "ModifyGizmo showAxisCue=0" Execute "RemoveFromGizmo displayItemNumber=2" elseif (stringmatch(popStr,"Re-Compile")) Execute "ModifyGizmo compile" elseif (stringmatch(popStr,"Show Info Window")) Execute "ModifyGizmo showInfo" else return 1 endif PopupMenu GizmoPopup mode=1 End Function ExplodeProc(ctrlName,varNum,varStr,varName) : SetVariableControl String ctrlName Variable varNum String varStr String varName if (!stringmatch(ctrlName,"explode") || varNum<0 || numtype(varNum)) return 1 endif Variable explode = varNum if (explode!=0) // when exploded, hide dots and cursor Execute "ModifyGizmo modifyObject=sphereCursor, property={radius,0.0}" if (strlen(getGrainPropertyFromObject("scatterBndry","size"))) Execute "ModifyGizmo ModifyObject=scatterBndry property={ size,0}" endif endif Variable xc0=NaN,yc0=NaN,zc0=NaN String gName, noteStr, gizmoNote = GetGizmoNote() gName = StringByKey("grainBndry1",gizmoNote,"=",":") Wave grain =$getGrainWaveFromName(gName) if (WaveExists(grain)) noteStr = note(grain) xc0 = NumberByKey("grain_xc",noteStr,"=") // center of explosion in 'grain' yc0 = NumberByKey("grain_yc",noteStr,"=") zc0 = NumberByKey("grain_zc",noteStr,"=") endif if (numtype(xc0+yc0+zc0)) // get center from gizmo note xc0 = NumberByKey("xc",gizmoNote,"=",":") // center of explosion is mean center yc0 = NumberByKey("yc",gizmoNote,"=",":") zc0 = NumberByKey("zc",gizmoNote,"=",":") endif if (numtype(xc0+yc0+zc0)) DoAlert 0, "failed to find center of mass in ExplodeProc()" return 1 endif String grainList = getGrainNameList() // list of grain names from the Gizmo iso-surface plot Variable N=ItemsInLIst(grainList) if (N<1) DoAlert 0, "no grains found in ExplodeProc()" return 1 endif // printf "in ExplodeProc, the control '%s' sent a value of %g\r",ctrlName,varNum // print " " // print "gName = ",gName, " explode=",explode // print "xc0,yc0,zc0 =",xc0,yc0,zc0,"µm" // print "grainList =",grainList Variable i,xc,yc,zc,x0,y0,z0,factor=.1 // move 0.1µm for every 1µm of Æxc for (i=0;i=0) break endif i += 1 while(1) i = strsearch(item,"property={ backColor,",0)+21 item = item[i,Inf] Variable r,g,b,alpha sscanf item,"%g,%g,%g,%g",r,g,b,alpha String cmd if (!cmpstr("popupGrainDim",ctrlName)) // dim the grain 'popStr' sprintf cmd, "ModifyGizmo ModifyObject=%s property={ backColor,%g,%g,%g,%g}",popStr,r,g,b,0.1 Execute cmd elseif (!cmpstr("popupGrainBrite",ctrlName)) // brighten the grain 'popStr' sprintf cmd, "ModifyGizmo ModifyObject=%s property={ backColor,%g,%g,%g,%g}",popStr,r,g,b,0.9 Execute cmd elseif (!cmpstr("popupGrainRestore",ctrlName)) // restore the grain 'popStr' // find the correct alpha String wName = getGrainWaveFromName(popStr) Wave wav = $wName alpha = NumberByKey("alpha",note(wav),"=") alpha = numtype(alpha) ? 0.6 : alpha sprintf cmd, "ModifyGizmo ModifyObject=%s property={ backColor,%g,%g,%g,%g}",popStr,r,g,b,alpha Execute cmd endif Killwaves/Z TW_gizmoObjectList KillStrings/Z S_gizmoObjectList End Function ButtonGrainColorProc(ctrlName) : ButtonControl String ctrlName String grainList = getGrainNameList() // list of grain names from the Gizmo iso-surface plot if (strlen(grainList)<1) DoAlert 0, "no grains found in ButtonGrainColorProc" return 1 endif Variable alpha if (!cmpstr(ctrlName,"buttonAllDim")) alpha = 0.05 elseif (!cmpstr(ctrlName,"buttonAllSolid")) alpha = 1 elseif (!cmpstr(ctrlName,"buttonAllRestore")) alpha = -1 // flag to use the original grains alpha else return 1 endif // now apply alpha to all of the grains String grain, cmd, wName Variable r,g,b,a Variable i,N=ItemsInList(grainList) Execute "ModifyGizmo startRecMacro" for (i=0;i=0,"root:raw:grainNo","grainNo") Prompt grainNoWave, "3D wave with all grain numbers", popup,PromptList DoPrompt "pick grainNo",grainNoWave if (V_flag) grainNoWave = "" endif Wave grainNo = $grainNoWave endif return GetWavesDataFolder(grainNo,2) End Function/S getGrainNameList() // returns the list of iso-surface names from a Gizmo iso-surface plot Execute "GetGizmo gizmoName" // check if a gizmo is up SVAR S_GizmoName=S_GizmoName if (strlen(S_GizmoName)<1) DoAlert 0, "No Gizmo Windows Open" return "" endif Execute "GetGizmo objectList" SVAR S_gizmoObjectList=S_gizmoObjectList String grainList="", item Variable i=0 do item = StringFromList(i,S_gizmoObjectList) if (strlen(item)<1) break elseif (strsearch(item,"AppendToGizmo isoSurface=",0)>=0) grainList += StringByKey("name",item,"=",",")+";" endif i += 1 while(1) Killwaves/Z TW_gizmoObjectList KillStrings/Z S_gizmoObjectList return grainList End Function/S getGrainNumbersList() // returns the list of waves making the iso-surface from a Gizmo iso-surface plot String grainList = "" String wName, wList = getGrainWaveList() Variable i,j for(i=0,wName="x";strlen(wName);i+=1) wName = StringFromList(i,wList) if (exists(wName)!=1) continue endif j=NumberByKey("grainNum",note($wName),"=") if (!numtype(j)) grainList = AddListItem(num2istr(j),grainList,";",Inf) endif endfor return grainList End Function/S getGrainWaveList() // returns the list of waves making the iso-surface from a Gizmo iso-surface plot Execute "GetGizmo gizmoName" // check if a gizmo is up SVAR S_GizmoName=S_GizmoName if (strlen(S_GizmoName)<1) DoAlert 0, "No Gizmo Windows Open" return "" endif Execute "GetGizmo objectList" SVAR S_gizmoObjectList=S_gizmoObjectList String grainList="", item Variable i=0 do item = StringFromList(i,S_gizmoObjectList) if (strsearch(item,"AppendToGizmo isoSurface=",0)>=0) item = RemoveFromList("\tAppendToGizmo",item," ") item = StringByKey("isoSurface",item,"=",",") // the file name if (strlen(item)>=1) grainList += item+";" endif endif i += 1 while(strlen(item)>=1) Killwaves/Z TW_gizmoObjectList KillStrings/Z S_gizmoObjectList return grainList End Function/S getGrainWaveFromName(objName) // returns the wave name for object objName String objName // name of object to get wave name for Execute "GetGizmo gizmoName" // check if a gizmo is up SVAR S_GizmoName=S_GizmoName if (strlen(S_GizmoName)<1) DoAlert 0, "No Gizmo Windows Open" return "" endif Execute "GetGizmo objectList" SVAR S_gizmoObjectList=S_gizmoObjectList String wName="", item Variable i=0 do item = StringFromList(i,S_gizmoObjectList)+"," if (strlen(item)<2) break elseif (strsearch(item,"AppendToGizmo isoSurface=",0)<0) i += 1 continue elseif (strsearch(item,"name="+objName+",",0)<0) i += 1 continue endif // found it i = strsearch(item,"isoSurface=",0) wName = StringByKey("isoSurface", item[i,Inf],"=",",") break while(1) Killwaves/Z TW_gizmoObjectList KillStrings/Z S_gizmoObjectList return wName End Function/S getGrainPropertyFromObject(objName,property) // returns the value of property for object objName String objName // name of object to get property of String property // property to get Execute "GetGizmo gizmoName" // check if a gizmo is up SVAR S_GizmoName=S_GizmoName if (strlen(S_GizmoName)<1) return "" endif Execute "GetGizmo objectList" SVAR S_gizmoObjectList=S_gizmoObjectList String search, value = "" sprintf search, "ModifyGizmo ModifyObject=%s property={ %s,",objName,property Variable i = strsearch(S_gizmoObjectList,search,0) if (i>=0) // found it i += strlen(search) value = S_gizmoObjectList[i,Inf] i = strsearch(value,"}",0) value = value[0,i-1] endif if (strlen(value)==0) String item i=0 do item = StringFromList(i,S_gizmoObjectList) if (strsearch(item,"\tAppendToGizmo ",0)>=0) item = RemoveFromList("\tAppendToGizmo",item," ") if (stringmatch(StringByKey("name",item,"=",","),objName)) // check for matching name value =StringByKey(property,item,"=",",") break endif endif i += 1 while(strlen(item)>=1) endif Killwaves/Z TW_gizmoObjectList KillStrings/Z S_gizmoObjectList return value End // ========================================================================= // ========================================================================= // ========================================================================= // Start of Grain Boundary Analysis // ========================================================================= // This routine goes through each grain and checks each of its neighbors, if a pair matches the criterion // for a sigma N boundary then it prints it out. To match a sigma N, the axis has to be aligned to within // angleTol, and the total rotation also has to be within angleTol. Note, this routine skips grains that are // smaller than minVol. Function checkAllSigmaBoundarys(grains,minVol,minBndryPoints,angleTol) Wave grains // 3d wave with all of the grain numbers (e.g. gNeighbor) Variable minVol // ignore grains smaller than this Variable minBndryPoints // mininum number of points in common needed for inclusion of a neighbor Variable angleTol // angle tolerance (degrees) if (!WaveExists(grains) || WaveDims(grains)!=3 || !(minVol>=0) || !(angleTol>0) || !(minBndryPoints>=0)) minVol = minVol>0 ? minVol : 20 angleTol = angleTol>0 ? angleTol : 15 minBndryPoints = !(minBndryPoints>=0) ? 1 : minBndryPoints String grainName=NameOfWave(grains) Prompt grainName,"3d wave with all grains",popup,ListMultiGrainMats() Prompt minVol, "min volume for a grain" Prompt angleTol, "angular tolerance for rotation and axis alignment (degreee), for Brandon Criterion use 15" Prompt minBndryPoints, "only include neighbors with at least this many bordering voxels" DoPrompt "grain array",grainName,minVol,minBndryPoints,angleTol if (V_flag) return 1 endif Wave grains = $grainName if (ItemsInList(GetRTStackInfo(0))<2) printf " checkAllSigmaBoundarys(%s,%g,%g,%g)\r",GetWavesDataFolder(grains,2),minVol,minBndryPoints,angleTol endif endif if (!WaveExists(grains) || WaveDims(grains)!=3 || !(angleTol>=0) || !(angleTol>0) || !(minBndryPoints>=0)) return 1 endif String gName = GetWavesDataFolder(grains,2) Wave gVol=$(gName+"_Vol") // number of voxels in the grain Variable N = numpnts(gVol) Variable totalAngle // total rotation angle between grains (degrees) Variable j,i,xx,yy,zz,Sigma Variable angleErr,axisErr // error in angle and axis alignment for a particular SigmaN String range,list String wName wName = UniqueName("sigmaAxis",1,0) ; Make/N=3/D $wName ; Wave sigmaAxis=$wName String degree = StrVarOrDefault("root:Packages:Grains:degree","¡") printf "Sigma\t\t i\tj\t\t angle(%s)\t\t\t\t\t(hkl)\t\t\t\t\t\t Æaxis%s\t\t\t Æangle%s\r",degree,degree,degree for (i=0;i=minVol;j = NextInRange(range,j)) if (j==i) continue endif list = BoundaryStats(gNeighbor,j,i,Inf) if (NumberByKey("Nboundary1",list,"=")0) integerDirectionExact(sigmaAxis,12) sigmaAxis = abs(sigmaAxis) Sort sigmaAxis, sigmaAxis printf "%g \t\t%d\t%d\t\t%8.4f\t\t(%8.5f, %8.5f, %8.5f)\t\t%7.3f\t\t\t%+7.3f\r"Sigma,i,j,totalAngle,sigmaAxis[0],sigmaAxis[1],sigmaAxis[2],axisErr, angleErr endif while(Sigma>0) endfor endfor KillWaves/Z sigmaAxis End //Function checkAllSigmaBoundarys(grains,minVol,angleTol) // Wave grains // 3d wave with all of the grain numbers (e.g. gNeighbor) // Variable minVol // ignore grains smaller than this // Variable angleTol // angle tolerance (degrees) // if (!WaveExists(grains) || WaveDims(grains)!=3 || !(angleTol>=0) || !(angleTol>0)) // minVol = minVol>0 ? minVol : 20 // angleTol = angleTol>0 ? angleTol : 1 // String grainName=NameOfWave(grains) // Prompt grainName,"3d wave with all grains",popup,ListMultiGrainMats() // Prompt minVol, "min volume for a grain" // Prompt angleTol, "angular tolerance for rotation and axis alignment (degreee)" // DoPrompt "grain array",grainName,minVol,angleTol // if (V_flag) // return 1 // endif // Wave grains = $grainName // if (ItemsInList(GetRTStackInfo(0))<2) // printf " checkAllSigmaBoundarys(%s,%g,%g)\r",GetWavesDataFolder(grains,2),minVol,angleTol // endif // endif // if (!WaveExists(grains) || WaveDims(grains)!=3 || !(angleTol>=0) || !(angleTol>0)) // return 1 // endif // String gName = GetWavesDataFolder(grains,2) // // Wave gVol=$(gName+"_Vol") // number of voxels in the grain // Variable N = numpnts(gVol) // Variable totalAngle // total rotation angle between grains (degrees) // Variable j,i,xx,yy,zz,sig // String range,list // String wName // wName = UniqueName("sigmaAxis",1,0) ; Make/N=3/D $wName ; Wave sigmaAxis=$wName // String degree = StrVarOrDefault("root:Packages:Grains:degree","¡") // printf "sigma\t i\tj\t\t angle(%s)\t\t\t(hkl)\r",degree // for (i=0;i=minVol;j = NextInRange(range,j)) // if (j==i) // continue // endif // list = BoundaryStats(gNeighbor,j,i,Inf) // sscanf StringByKey("hklAxis",list,"="), "%g,%g,%g",xx,yy,zz // sigmaAxis = {xx,yy,zz} // totalAngle = NumberByKey("totalAngle",list,"=") // sig = sigmaType(totalAngle,sigmaAxis,angleTol) // // integerDirectionExact(sigmaAxis,12) // sigmaAxis = abs(sigmaAxis) // Sort sigmaAxis, sigmaAxis // if (sig>0) // printf "%d \t%d\t%d\t\t%8.4f\t\t(%8.5f, %8.5f, %8.5f)\r"sig,i,j,totalAngle,sigmaAxis[0],sigmaAxis[1],sigmaAxis[2] // endif // endfor // endfor // KillWaves/Z sigmaAxis //End // This routine is only called by checkAllSigmaBoundarys(). It looks at the given axis and angle and // returns the sigma N type. If it is not a match to any type, it returns 0. Static Function sigmaType(angle,axisIn,angleTol,angleErr,axisErr,Sigma) Variable angle // measured total rotation angle (degrees) Wave axisIn // measured axis of rotation given Variable angleTol // angle tolerance (degrees), actual tolerance is angleTol/sqrt(SigmaN) // for Brandon Criterion, angleTol=15¡ Variable &angleErr, &axisErr // actual error in angle and axis for this Sigma Variable &Sigma // on entry, it is the last Sigma, on exit the result // first time use a value of Sigma²0, for subsequent calls, Sigma=lastSigma Variable lastSigma=Sigma+1e-4 Sigma = 0 angle = abs(angle) if (angle sigmaCSL[i][0]) // skip SigmaN that were already checked continue endif hkl = sigmaCSL[i][2+p] normalize(hkl) Brandon = angleTol/sqrt(sigmaCSL[i][0]) angleErr = (angle - sigmaCSL[i][1]) axisErr = acos(max(-1,min(MatrixDot(axis,hkl),1))) * 180/PI delta = 2*atan( sqrt(tan(angleErr/2*PI/180)^2 + tan(axisErr/2*PI/180)^2) )* 180/PI if (delta < Brandon) Sigma = sigmaCSL[i][0] Sigma = round(Sigma*100)/100 break endif endfor KillWaves/Z hkl,axis return Sigma End //Static Function sigmaType(angle,axisIn,angleTol) // Variable angle // total rotation angle (degrees) // Wave axisIn // axis of rotation given // Variable angleTol // angle tolerance (degrees) // // angle = abs(angle) // if (angle cosMin // String wName // wName = UniqueName("hkl",1,0) ; Make/N=3/D $wName ; Wave hkl=$wName // wName = UniqueName("axis",1,0) ; Make/N=3/D $wName ; Wave axis=$wName // axis = abs(axisIn) // make a "normal form" of axis, all positive and ordered // Sort axis, axis // normalize(axis) // and normalized too // // Wave sigmaCSL = root:Packages:Grains:sigmaCSL // if (!WaveExists(sigmaCSL)) // initGrainGizmo() // this will make sigmaCSL // endif // // Variable i, sigma = 0 // for (i=0;icosMin && abs(angle-sigmaCSL[i][1])maxGrain || i2>maxGrain || i1==i2 || numtype(radius)==2 || radius<=0) if (ItemsInList(GetRTStackInfo(0))<2) DoAlert 0,"invalid inputs" endif return "" endif // check that grains are neighbors String list = makeBorderList(grains,i2) if (WhichListItem(num2istr(i1),list)<0) if (ItemsInList(GetRTStackInfo(0))<2) sprintf str, "grains %d and %d are not neighbors\r",i1,i2 printf str DoAlert 0, str endif return "" endif // all checks are done, now do the work String fName = GetWavesDataFolder(grains,1) Wave indicies = $(fname+"raw:indicies") Wave XYZindex = $(fname+"raw:XYZindex") Wave OrientMat = $(fname+"raw:OrientMat") String gName = GetWavesDataFolder(grains,2) Wave COMsingle = $(gName+"_COM") Variable Nx,Ny,Nz Nx=DimSize(XYZindex,0) Ny=DimSize(XYZindex,1) Nz=DimSize(XYZindex,2) String out="" out=ReplaceNumberByKey("i1",out,i1,"=") out=ReplaceNumberByKey("i2",out,i2,"=") Wave g1 = $getGrainCoords(grains,i1) // list of grain coords for each grain Wave g2 = $getGrainCoords(grains,i2) Variable N1=DimSize(g1,0), N2=DimSize(g2,0) Variable single1 = grainCOMsingle(g1) // single index to a measured point nearest to COM Variable single2 = grainCOMsingle(g2) Variable totalAngle = angleBetweenPointsSymReduced(single1,single2) out=ReplaceNumberByKey("totalAngle",out,totalAngle,"=") Make/N=(N2)/T/O g21neighgors // contains a list of neighbors to each grain in g2 (neighbors are in g2) g21neighgors = "" Variable i,j, ix0,iy0,iz0, ix,iy,iz Variable Nbndry1=0, Nbndry2=0, Nadd str=UniqueName("offsets",1,0) // offsets[][] contains the 6 voxels to the 1st nearest neighbors (faces in common) Make/N=(6,3) $str Wave offsets=$str offsets[0][0]= {-1,1, 0,0, 0,0} offsets[0][1]= { 0,0,-1,1, 0,0} offsets[0][2]= { 0,0, 0,0,-1,1} Variable Noffset = DimSize(offsets,0) for (i=0;i=Nx || iy>=Ny || iz>=Nz || ix<0 || iy<0 || iz<0) continue // point is NaN or out of range elseif (grains[ix][iy][iz]==i1) sprintf str "%d,%d,%d",ix,iy,iz g21neighgors[i] = AddListItem(str, g21neighgors[i]) Nadd = 1 endif endfor Nbndry2 += Nadd endfor Variable jlast=-1 // remove all non-surface points from g2 and compress for (i=0;i0) g2[i][0,2] = g2[j][q] jlast = j break endif endfor endfor Redimension/N=(Nbndry2,3) g2 Make/N=(N1)/O g1_flag_temp__ g1_flag_temp__ = 0 Variable k for (i=0;i0) str = g21neighgors[i] for (j=0;j1e-5) // cursor is active (since radius is >0) xc = NumVarOrDefault("root:Packages:Grains:Xcursor",0) yc = NumVarOrDefault("root:Packages:Grains:Ycursor",0) zc = NumVarOrDefault("root:Packages:Grains:Zcursor",0) else if (Nbndry1>=3) Make/N=(Nbndry1)/O temp_sum_up_ temp_sum_up_ = g1[p][0] ; xc = faverage(temp_sum_up_) temp_sum_up_ = g1[p][1] ; yc = faverage(temp_sum_up_) temp_sum_up_ = g1[p][2] ; zc = faverage(temp_sum_up_) endif if (Nbndry2>=3) Make/N=(Nbndry2)/O temp_sum_up_ temp_sum_up_ = g2[p][0] ; xc = (xc+faverage(temp_sum_up_))/2 temp_sum_up_ = g2[p][1] ; yc = (yc+faverage(temp_sum_up_))/2 temp_sum_up_ = g2[p][2] ; zc = (zc+faverage(temp_sum_up_))/2 endif endif out=ReplaceNumberByKey("xc",out,xc,"=") out=ReplaceNumberByKey("yc",out,yc,"=") out=ReplaceNumberByKey("zc",out,zc,"=") // refilter g1 and g2 removing all points more than distance radius from (xc,yc,zc) Variable rad2 = radius*radius for (i=0;irad2) g1[i][] = NaN // if a point is farther than radius from (xc,yc,zc) set to NaN endif endfor DeleteNaNs(g1) // remove the NaNs just added, and compress for (i=0;irad2) g2[i][] = NaN // if a point is farther than radius from (xc,yc,zc) set to NaN endif endfor DeleteNaNs(g2) // remove the NaNs just added, and compress Make/N=3/D/O normal1,normal2, normal12 // vector in the average surface normal for each grain (beam line coords) NormalOfPlane(g1,normal1) // normal1 is in beam line coords normalize(normal1) NormalOfPlane(g2,normal2) // normal2 is in beam line coords normalize(normal2) Duplicate/O g1 bndryScatterPoints Variable normalAngleErr = acos(max(min(MatrixDot(normal1,normal2),1),-1)) if (!numtype(normalAngleErr)) out=ReplaceNumberByKey("normalAngleErr",out,normalAngleErr,"=") endif normal12 = (normal1+normal2)/2 // average normal to boundary String degree = StrVarOrDefault("root:Packages:Grains:degree","¡") if (normalAngleErr*180/PI>5 && ItemsInList(GetRTStackInfo(0))<2) // if (normalAngleErr*180/PI>5 && !stringmatch(StringFromList(0, GetRTStackInfo(0)),"GizmoAroundOneGrain")) DoAlert 0, "WARNING in BoundaryStats(), angle between normals > 5"+degree+", consider reducing radius" normal12 = normal1 endif if (!numtype(normal12[0])) sprintf str, "%g,%g,%g",normal12[0],normal12[1],normal12[2] out=ReplaceStringByKey("bndryNormal",out,str,"=") endif Variable maxhkl=12 Make/N=(3,3)/O mat1,mat2 i = COMsingle[i1] mat1 = OrientMat[i][p][q] i = COMsingle[i2] mat2 = OrientMat[i][p][q] Make/N=3/O/D axisOfMat // get the axis of the rotation Make/N=(3,3)/O/D BASi // BASi, total rotation matrix from A to B, cubic reduced symReducedRotation(mat1,mat2,BASi) // BASi = (B Inv(A) S^i) = (mat2 Inv(mat1) S^i), A=mat1, B=mat2 axisOfMatrix(BASi,axisOfMat) // make the axis of the rotation matrix BASi (grain2 coordinates) Make/N=3/O/D hklAxis // hkl of total rotation matrix axis, in terms of grains1 hklAxis = -axisOfMat // change from grain2 to grain1 (for the axis they are just opposite) integerDirectionExact(hklAxis,maxhkl) // hkl of rotation axis in terms of grain1 MatrixOp/O tempAxisOfMat = Inv(mat2) x axisOfMat// transform from grain2(hkl) coords to sample (xyz) axisOfMat = tempAxisOfMat TransformSample2Beamline(axisOfMat) // transform axisOfMat from sample (xyz) -> beamline (XYZ) sprintf str, "%g,%g,%g",hklAxis[0],hklAxis[1],hklAxis[2] out=ReplaceStringByKey("hklAxis",out,str,"=") sprintf str, "%g,%g,%g",axisOfMat[0],axisOfMat[1],axisOfMat[2] out=ReplaceStringByKey("axisOfMat",out,str,"=") // axisOfMatrix(BASi,hklAxis) // make the axis of the rotation matrix BASi (sample coords, xyz) /// hklAxis *= -1 // change from grain2 to grain1 // integerDirectionApprox(hklAxis,maxhkl) // hkl of rotation axis in terms of grain1 // axisOfMatrix(BASi,axisOfMat) // make the axis of the rotation matrix BASi (sample coords, xyz) // TransformSample2Beamline(axisOfMat) // transform axisOfMat from sample (xyz) -> beamline (XYZ) Variable twist=naN,tilt=NaN, angleBetweenAxes=NaN// twist and tilt angles for the boundary (degree) if (normalize(normal12)>0.1) // if normal12 is valid, then proceed to calculate the hkl1 & hkl2 // next find the hkl for each of the two grains that lies along normal12 Make/N=3/O/D normalXHF // XYZ -> xyz, convert normal from beamline coords (XYZ) to the sample (xyz) == (X -H -F) normalXHF = normal12 TransformBeamline2Sample(normalXHF) MatrixOp/O hkl1 = mat1 x normalXHF // A*(xyz) = (hkl) integerDirectionApprox(hkl1,maxhkl) MatrixOp/O hkl2 = mat2 x normalXHF integerDirectionApprox(hkl2,maxhkl) Variable cosine = abs(MatrixDot(axisOfMat,normal12)) angleBetweenAxes = acos(min(cosine,1))*180/PI TiltTwistOfBoundary(mat1,mat2,normalXHF,twist,tilt) // find the twist and tilt of the boundary (degree)) sprintf str, "%g,%g,%g",hkl1[0],hkl1[1],hkl1[2] out=ReplaceStringByKey("hkl1",out,str,"=") sprintf str, "%g,%g,%g",hkl2[0],hkl2[1],hkl2[2] out=ReplaceStringByKey("hkl2",out,str,"=") endif if (ItemsInList(GetRTStackInfo(0))<2) printf " for the boundary between grains %g and %g in '%s':\r",i1,i2,NameOfWave(grains) printf " angle between grains %d and %d is %g%s, rotated about hkl=(%g, %g, %g) of grain1,",i1,i2,totalAngle,degree,hklAxis[0],hklAxis[1],hklAxis[2] printf " --> (beamLine coords)=(%g, %g, %g)\r", axisOfMat[0],axisOfMat[1],axisOfMat[2] if (!numtype(angleBetweenAxes)) printf " angle between axis of rotation and interface normal is %g%s (after cubic symmetry operations),",angleBetweenAxes,degree printf " tilt=%g%s, twist=%g%s",tilt,degree,twist,degree Variable tiltTwistPure = 5 // threshold to decide whether to flag boundary as twist or tilt if (abs(angleBetweenAxes-90) {%g, %g, %g} and grain2-> {%g, %g, %g}\r",normal1[0],normal1[1],normal1[2],normal2[0],normal2[1],normal2[2] printf " normals use voxels out to %g %s away from point (%g, %g, %g)%s\r",radius,micron,xc,yc,zc,micron printf " approx surface normals are grain1->(%g %g %g) and grain2->(%g %g %g) (with an hkl limit of %g)\r",hkl1[0],hkl1[1],hkl1[2],hkl2[0],hkl2[1],hkl2[2],maxhkl hkl1 = abs(hkl1) ; Sort hkl1, hkl1 hkl2 = abs(hkl2) ; Sort hkl2, hkl2 printf " or grain1->(%g %g %g) and grain2->(%g %g %g)\r",hkl1[0],hkl1[1],hkl1[2],hkl2[0],hkl2[1],hkl2[2] endif endif KillWaves/Z hkl1,hkl2,mat1,mat2, axisOfMat,tempAxisOfMat, rotAB, normalXHF,BASi,hklAxis KillWaves/Z W_coef,W_sigma,W_ParamConfidenceInterval,temp_sum_up_ KillWaves/Z g1,g2,g21neighgors,normal1,normal12,normal2,offsets return out End Static Function DeleteNaNs(g) Wave g Variable N=DimSize(g,0), M=DimSize(g,1) Variable iold, inew for (iold=0, inew=0;ioldmaxGrain || i2>maxGrain || i1==i2) // if (ItemsInList(GetRTStackInfo(0))<2) // DoAlert 0,"invalid inputs" // endif // return "" // endif // // check that grains are neighbors // String list = makeBorderList(grains,i2) // if (WhichListItem(num2istr(i1),list)<0) // if (ItemsInList(GetRTStackInfo(0))<2) // sprintf str, "grains %d and %d are not neighbors\r",i1,i2 // printf str // DoAlert 0, str // endif // return "" // endif // // all checks are done, now do the work // // String fName = GetWavesDataFolder(grains,1) // Wave indicies = $(fname+"raw:indicies") // Wave XYZindex = $(fname+"raw:XYZindex") // Wave OrientMat = $(fname+"raw:OrientMat") // String gName = GetWavesDataFolder(grains,2) // Wave COMsingle = $(gName+"_COM") // // Variable Nx,Ny,Nz // Nx=DimSize(XYZindex,0) // Ny=DimSize(XYZindex,1) // Nz=DimSize(XYZindex,2) // String out="" // out=ReplaceNumberByKey("i1",out,i1,"=") // out=ReplaceNumberByKey("i2",out,i2,"=") // // Wave g1 = $getGrainCoords(grains,i1) // list of grain coords for each grain // Wave g2 = $getGrainCoords(grains,i2) // Variable N1=DimSize(g1,0), N2=DimSize(g2,0) // Variable single1 = grainCOMsingle(g1) // single index to a measured point nearest to COM // Variable single2 = grainCOMsingle(g2) // // Variable totalAngle = angleBetweenPointsSymReduced(single1,single2) // out=ReplaceNumberByKey("totalAngle",out,totalAngle,"=") // // Make/N=(N2)/T/O g21neighgors // contains a list of neighbors to each grain in g2 (neighbors are in g2) // g21neighgors = "" // Variable i,j, ix0,iy0,iz0, ix,iy,iz // Variable Nbndry1=0, Nbndry2=0, Nadd // // str=UniqueName("offsets",1,0) // // offsets[][] contains the 6 voxels to the 1st nearest neighbors (faces in common) // Make/N=(6,3) $str // Wave offsets=$str // offsets[0][0]= {-1,1, 0,0, 0,0} // offsets[0][1]= { 0,0,-1,1, 0,0} // offsets[0][2]= { 0,0, 0,0,-1,1} // Variable Noffset = DimSize(offsets,0) // // for (i=0;i=Nx || iy>=Ny || iz>=Nz || ix<0 || iy<0 || iz<0) // continue // point is NaN or out of range // elseif (grains[ix][iy][iz]==i1) // sprintf str "%d,%d,%d",ix,iy,iz // g21neighgors[i] = AddListItem(str, g21neighgors[i]) // Nadd = 1 // endif // endfor // Nbndry2 += Nadd ////if (Nadd) ////print i," ",g2[i][0],g2[i][1],g2[i][2]," ",g21neighgors[i] ////endif // endfor // // Variable jlast=-1 // remove all non-surface points from g2 and compress // for (i=0;i0) // g2[i][0,2] = g2[j][q] // jlast = j // break // endif // endfor // endfor // Redimension/N=(Nbndry2,3) g2 // // Make/N=(N1)/O g1_flag_temp__ // g1_flag_temp__ = 0 // Variable k // for (i=0;i0) // str = g21neighgors[i] // for (j=0;j=3) // g1[][0] = g1[p][0]*dx + x0 // first do grain1 // g1[][1] = g1[p][1]*dy + y0 // g1[][2] = g1[p][2]*dz + z0 // Make/N=(Nbndry1)/O temp_sum_up_ // temp_sum_up_ = g1[p][0] ; xc = faverage(temp_sum_up_) // temp_sum_up_ = g1[p][1] ; yc = faverage(temp_sum_up_) // temp_sum_up_ = g1[p][2] ; zc = faverage(temp_sum_up_) // NormalOfPlane(g1,normal1) ////print NameOfWave(g1),xc,yc,zc,g1,normal1 // normalize(normal1) // Duplicate/O g1 bndryScatterPoints // endif // if (Nbndry2>=3) // g2[][0] = g2[p][0]*dx + x0 // second do grain2 // g2[][1] = g2[p][1]*dy + y0 // g2[][2] = g2[p][2]*dz + z0 // Make/N=(Nbndry2)/O temp_sum_up_ // temp_sum_up_ = g2[p][0] ; xc = (xc+faverage(temp_sum_up_))/2 // temp_sum_up_ = g2[p][1] ; yc = (yc+faverage(temp_sum_up_))/2 // temp_sum_up_ = g2[p][2] ; zc = (zc+faverage(temp_sum_up_))/2 // NormalOfPlane(g2,normal2) // normalize(normal2) ////print NameOfWave(g2),xc,yc,zc,g2,normal1 // out=ReplaceNumberByKey("xc",out,xc,"=") // out=ReplaceNumberByKey("yc",out,yc,"=") // out=ReplaceNumberByKey("zc",out,zc,"=") // endif // Variable normalAngleErr = acos(max(min(MatrixDot(normal1,normal2),1),-1)) // out=ReplaceNumberByKey("normalAngleErr",out,normalAngleErr,"=") // normal12 = (normal1+normal2)/2 // average normal to boundary // if (normalAngleErr*180/PI>5 && !stringmatch(StringFromList(0, GetRTStackInfo(0)),"GizmoAroundOneGrain")) // DoAlert 0, "WARNING in BoundaryStats(), angle between normals > 5¡" // normal12 = normal1 // endif // sprintf str, "%g,%g,%g",normal12[0],normal12[1],normal12[2] // out=ReplaceStringByKey("bndryNormal",out,str,"=") // // Variable maxhkl=12 // if (normalize(normal12)>0.1) // if normal12 is valid, then proceed to calculate the hkl1 & hkl2 // // next find the hkl for each of the two grains that lies along normal12 // Make/N=3/O/D hkl1,hkl2, normalXHF // Make/N=(3,3)/O mat1,mat2 // // XYZ -> xyz, convert normal from beamline coords (XYZ) to the sample (xyz) == (X -H -F) // normalXHF[0] = normal12[0] // normalXHF[1] = -(normal12[1]+normal12[2])/sqrt(2) // normalXHF[2] = (normal12[1]-normal12[2])/sqrt(2) // i = COMsingle[i1] // // print "total rotaion angle for mat1 is ",anglefromMatrix(i) // mat1 = OrientMat[i][p][q] // i = COMsingle[i2] // // print "total rotaion angle for mat2 is ",anglefromMatrix(i) // mat2 = OrientMat[i][p][q] // MatrixOp/O hkl1 = mat1 x normalXHF // A*(xyz) = (hkl) // integerDirectionApprox(hkl1,maxhkl) // MatrixOp/O hkl2 = mat2 x normalXHF // integerDirectionApprox(hkl2,maxhkl) // Make/N=3/O/D axisOfMat // get the axis of the rotation // MatrixOp/O rot = Inv(mat1) x mat2 // rot is the total rotation matrix // Variable angle = axisOfMatrix(rot,axisOfMat) // compute axis and angle // // xyz -> XYZ, convert from sample (xyz) to beamline coords (XYZ), and again (xyz) == (X -H -F) // Variable ysample = axisOfMat[1], zsample = axisOfMat[2] // axisOfMat[1] = (-ysample+zsample)/sqrt(2) // axisOfMat[2] = (-ysample-zsample)/sqrt(2) // Variable angleBetweenAxes = acos(MatrixDot(axisOfMat,normal12))*180/PI // angleBetweenAxes = min(angleBetweenAxes, 180-angleBetweenAxes) // sprintf str, "%g,%g,%g",hkl1[0],hkl1[1],hkl1[2] // out=ReplaceStringByKey("hkl1",out,str,"=") // sprintf str, "%g,%g,%g",hkl2[0],hkl2[1],hkl2[2] // out=ReplaceStringByKey("hkl2",out,str,"=") // endif // // if (ItemsInList(GetRTStackInfo(0))<2) // printf " for the boundary between grains %g and %g in '%s':\r",i1,i2,NameOfWave(grains) // printf " angle between grains %d and %d is %g¡, rotated about the (%g, %g, %g)\r",i1,i2,totalAngle,axisOfMat[0],axisOfMat[1],axisOfMat[2] // printf " angle between axis of rotation and interface normal is %g¡\r",angleBetweenAxes // printf " grain 1 has %d points in the surface, and grain 2 has %d in the surface\r",Nbndry1,Nbndry2 // printf " surface normal (average) between grains 1 & 2 is {%g, %g, %g} angle between individual normals is %.3g deg\r",normal12[0],normal12[1],normal12[2],normalAngleErr*180/PI // printf " surface normals are: grain1-> {%g, %g, %g} and grain2-> {%g, %g, %g}\r",normal1[0],normal1[1],normal1[2],normal2[0],normal2[1],normal2[2] // printf " approx surface normals are grain1->(%g %g %g) and grain2->(%g %g %g) (with an hkl limit of %g)\r",hkl1[0],hkl1[1],hkl1[2],hkl2[0],hkl2[1],hkl2[2],maxhkl // hkl1 = abs(hkl1) ; Sort hkl1, hkl1 // hkl2 = abs(hkl2) ; Sort hkl2, hkl2 // printf " or grain1->(%g %g %g) and grain2->(%g %g %g)\r",hkl1[0],hkl1[1],hkl1[2],hkl2[0],hkl2[1],hkl2[2] // endif // KillWaves/Z hkl1,hkl2,mat1,mat2, axisOfMat, rot, normalXHF // KillWaves/Z W_coef,W_sigma,W_ParamConfidenceInterval,temp_sum_up_ // KillWaves/Z g1,g2,g21neighgors,normal1,normal12,normal2,offsets // return out //End Function TiltTwistOfBoundary(Ain,B,normal,twist,tilt) // return the twist and tilt angles for this boundary Wave Ain,B // the two orientation matrices (in sample representation, NOT beamline) Wave normal // normal to interface (sample representation, NOT beamline!) Variable &twist,&tilt // angles returned (degree) String wName wName = UniqueName("hkl",1,0) ; Make/N=3/D $wName ; Wave hA=$wName wName = UniqueName("hkl",1,0) ; Make/N=3/D $wName ; Wave hB=$wName wName = UniqueName("SymMatrix",1,0) ; Make/N=(3,3)/D/O $wName ; Wave symOp = $wName wName = UniqueName("Amat",1,0) ; Make/N=(3,3)/D/O $wName ; Wave A = $wName A = Ain BestSymOp(A,B,symOp) MatrixOp/O A = Inv(symOp) x A // the new A, symmetry reduced MatrixOp/O hA = Normalize(A x normal) MatrixOp/O hB = Normalize(B x normal) if (MatrixDot(hA,hB)<0) hB *= -1 endif Cross hA, hB Wave W_Cross=W_Cross Variable asine = normalize(W_Cross) tilt = asin(asine)*180/PI wName = UniqueName("rot",1,0) Make/N=3/D $wName Wave tiltMat=$wName rotationMatAboutAxis(W_Cross,-tilt,tiltMat) MatrixOp/O hA = tiltMat x hA // this brings hA == hB wName = UniqueName("matrix1D",1,0) ; Make/N=(1)/D/O $wName ; Wave cosine11 = $wName MatrixOp/O cosine11 = (Trace(tiltMat x A x Inv(B))-1)/2 Variable cosine = min(max(-1,cosine11[0]),1) // ensure cosine in range [-1,1] twist = acos(cosine)*180/PI KillWaves/Z hA,hB, W_Cross, cosine11, tiltMat, symOp, A End Function integerDirectionExact(hkl,maxInt) // changes vector hkl to be an approxiamtely integer vector, like integerDirectionApprox(), but exact Wave hkl Variable maxInt String wName = UniqueName("hkl",1,0) Make/N=(numpnts(hkl))/O/D $wName Wave hkli = $wName WaveStats/Q/M=1 hkl Variable biggest = max(abs(V_max),abs(V_min)) hkli = round(hkl[p]*maxInt/biggest) Variable factor = gcf(GetWavesDataFolder(hkli,2)) hkl /= factor KillWaves/Z hkli End // Function integerDirectionApprox(hkl,maxInt) // changes vector hkl to be an integer vector, approximately in same direction Wave hkl Variable maxInt WaveStats/Q/M=1 hkl Variable biggest = max(abs(V_max),abs(V_min)) hkl = round(hkl[p]*maxInt/biggest) Variable factor = gcf(GetWavesDataFolder(hkl,2)) hkl /= factor End // //Function integerDirectionExact(hkl,maxInt) // changes vector hkl to be an approxiamtely integer vector, like integerDirectionApprox(), but exact // Wave hkl // Variable maxInt // Make/N=3/O/D test_hkl_ // normalize(hkl) // test_hkl_ = hkl // hkl = round(maxInt*hkl[p]) // Variable factor = gcf(GetWavesDataFolder(hkl,2)) // hkl /= factor // factor = norm(hkl)/norm(test_hkl_) // hkl = test_hkl_*factor // KillWaves/Z test_hkl_ //End // // //Function integerDirectionApprox(hkl,maxInt) // changes vector hkl to be an integer vector, approximately in same direction // Wave hkl // Variable maxInt // normalize(hkl) // hkl = round(maxInt*hkl[p]) // Variable factor = gcf(GetWavesDataFolder(hkl,2)) // hkl /= factor //End // // this revision of gcf uses the the "PrimeFactors" command available with Igor 5 Static Function gcf(factors) // find greatest common factor of a wave whose values are all integers String factors // either a list of factors or the name of a wave containing the factors String tempName="" Variable N,i if (exists(factors)==1 && strsearch(factors,";",0)<0) // passed a wave name Wave wav=$factors else N = ItemsInList(factors) // a list was passed if (N<1) return NaN endif tempName = UniqueName("tempPrimes",1,0) Make/N=(N) $tempName Wave wav=$tempName for (i=0;i=0) primeLists[i] = RemoveListItem(m,primeLists[i]) elseif (cmpstr(primeLists[i],"0;")) found=0 endif endfor if (found) gcf *= str2num(item) endif endfor KillWaves/Z primeLists,W_PrimeFactors, $tempName return gcf End Function NormalOfPlane(xyzPos,normal) Wave xyzPos Wave normal if (DimSize(xyzPos,0)<3) // need at least 3 points to define a plane normal = NaN return 1 endif Variable X1,Y1,Z1,XX,YY,ZZ,XY,XZ,YZ Variable N = DimSize(xyzPos,0) Make/N=(N)/O/D temp_for_plane_ Wave sumTemp = temp_for_plane_ sumTemp = xyzPos[p][0] ; X1 = sum(sumTemp) sumTemp = xyzPos[p][1] ; Y1 = sum(sumTemp) sumTemp = xyzPos[p][2] ; Z1 = sum(sumTemp) sumTemp = xyzPos[p][0]*xyzPos[p][0] ; XX = sum(sumTemp) sumTemp = xyzPos[p][1]*xyzPos[p][1] ; YY = sum(sumTemp) sumTemp = xyzPos[p][2]*xyzPos[p][2] ; ZZ = sum(sumTemp) sumTemp = xyzPos[p][0]*xyzPos[p][1] ; XY = sum(sumTemp) sumTemp = xyzPos[p][0]*xyzPos[p][2] ; XZ = sum(sumTemp) sumTemp = xyzPos[p][1]*xyzPos[p][2] ; YZ = sum(sumTemp) Make/N=3/D/O con1={X1,Y1,Z1} Make/N=(3,3)/D/O mat2 mat2[0][0]=XX ; mat2[0][1]=XY ; mat2[0][2]=XZ // note, mat2 is symmetric mat2[1][0]=XY ; mat2[1][1]=YY ; mat2[1][2]=YZ mat2[2][0]=XZ ; mat2[2][1]=YZ ; mat2[2][2]=ZZ MatrixLLS mat2 con1 Wave M_B = M_B Redimension/N=3/D normal normal = M_B // print xyzPos // print "x1,y1,z1=",x1,y1,z1 // printf "X1=%g, Y1=%g, Z1=%g\r",x1,y1,z1 // printf "XX=%g, YY=%g, ZZ=%g, XY=%g, XZ=%g, YZ=%g\r",XX,YY,ZZ,XY,XZ,YZ // print mat2 // MatrixMultiply mat2, M_B // Wave M_product=M_product // print con1,M_product // KillWaves/Z M_product KillWaves/Z M_B,M_A,temp_for_plane_,mat2,con1 return 0 End Function/S makeBorderList(grains,grainNum) // it includes grainNum in the list too Variable grainNum // find the grains that border grainsNum Wave grains // 3d wave with all of the grain numbers (e.g. gNeighbor) if (!WaveExists(grains) || numtype(grainNum) || grainNum<0) String grainName=NameOfWave(grains) Prompt grainName,"3d wave with all grains",popup,ListMultiGrainMats() grainNum = numtype(grainNum)==2 ? 0 : max(grainNum,0) Prompt grainNum, "grain number" DoPrompt "choose grain",grainName,grainNum if (V_flag) return "" endif Wave grains = $grainName endif if (!WaveExists(grains)) return "" endif WaveStats/Q grains Variable maxGrain = V_max if (numtype(grainNum) || grainNum<0 || grainNum>maxGrain) return "" endif Make/N=(maxGrain+1)/O tempGrainFoundList_,tempGrainFoundIndex_ tempGrainFoundList_ = 0 tempGrainFoundIndex_ = p String wName = getGrainCoords(grains,grainNum) // create a wave containing the [ix][iy][iz] coords of each point in grain Wave xyz = $wName Variable Nx,Ny,Nz Nx=DimSize(grains,0) Ny=DimSize(grains,1) Nz=DimSize(grains,2) // check the neighbor of each xyz[i][3] for new neighbors Variable Nxyz = DimSize(xyz,0) Variable i,j,ix,iy,iz for (i=0;i=1) ? minSize : 20 // String grainNumName="" // Prompt grainNumName, "array of gain id's",popup,WaveList("*grainNo",";","DIMS:3") // Prompt minSize, "min no. of voxels in a grain" // if (WaveExists(grainNo)) // DoPrompt "min grain size to count",minSize // else // DoPrompt "grain to count",grainNumName,minSize // Wave grainNo = $grainNumName // endif // if (V_flag) // return "" // endif // printf " GetNbestGrains(%s,%d)\r", GetWavesDataFolder(grainNo,2),minSize // endif // // String noteStr = note(grainNo) // Variable nGrains = NumberByKey("numGrains",noteStr,"=") // // String Ssizes=UniqueName("VoxSizes",1,0) // String Sindex=UniqueName("index",1,0) // Make/O/N=(nGrains) $Ssizes,$Sindex // Wave sizes = $Ssizes // Wave index = $Sindex // Histogram/B={0,1,nGrains} grainNo,sizes // index = p // Sort/R sizes, sizes,index // // String grainList="" // Variable i = 0 // do // if (sizes[i]0) dataFolderStr = dataFolderStr[0,i-1] endif dataFolderStr = CleanupName(dataFolderStr,0) if (DataFolderExists(":"+dataFolderStr)) DoAlert 0, "Data folder "+dataFolderStr+" already exists" SetDataFolder fldrSav return 1 endif NewDataFolder/S $(":"+dataFolderStr) NewDataFolder/O/S :raw readin = 1 else SetDataFolder (":"+dataFolderStr) NewDataFolder/O/S :raw DoAlert 2, "re-read in the data" if (V_flag==3) // "cancel" clicked SetDataFolder fldrSav return 1 endif readin = (V_flag==1) if (readin) printf "re-reading and overwriting existing data\r" endif endif endif if (readin) String columnInfoStr columnInfoStr = "C=1,N=XX;C=1,N=YY;C=1,N=ZZ;" columnInfoStr += "C=1,N=Hx;C=1,N=Hy;C=1,N=Hz;" columnInfoStr += "C=1,N=Kx;C=1,N=Ky;C=1,N=Kz;" columnInfoStr += "C=1,N=Lx;C=1,N=Ly;C=1,N=Lz;" // columnInfoStr += "C=1,N=Hx;C=1,N=Kx;C=1,N=Lx;" // this done because IDL says A[col,row], backwards, WRONG! // columnInfoStr += "C=1,N=Hy;C=1,N=Ky;C=1,N=Ly;" // columnInfoStr += "C=1,N=Hz;C=1,N=Kz;C=1,N=Lz;" columnInfoStr += "C=1,N=axisX;C=1,N=axisY;C=1,N=axisZ;C=1,N=angle;" LoadWave/A/B=columnInfoStr/G/O/P=home fullFilePath String wName,noteStr = "dataFile="+S_path+S_fileName for (i=0;i thresh) delta = findMode(dxw,thresh) delta = (numtype(delta) || (delta0) || strlen(outName)<1) DoAlert 0, "in make3dColorTable(), the wave 'vecList[][3]' does not exist or has wrong dimension, or other invalid input" return 1 endif Variable N = DimSize(vecList,0) Variable xlo=1e10,xhi=-1e10,ylo=1e10,yhi=-1e10,zlo=1e10,zhi=-1e10 Variable i for (i=0;i=1) ? minSize : 20 Prompt minSize, "min no. of voxels in a grain" String grainNoWave=NameOfWave(grains) Prompt grainNoWave,"3d wave with all grains",popup,ListMultiGrainMats() DoPrompt "grains to make" grainNoWave,minSize if (V_flag) return 1 endif Wave grains = $grainNoWave if (ItemsInList(GetRTStackInfo(0))<2) printf " NumOfGrainsBiggerThan(%s,%g)\r",NameOfWave(grains),minSize endif endif if (numtype(minSize) || minSize<1) return 0 endif if (!WaveExists(grains) || WaveDims(grains)!=3) DoAlert 0, "'grains' wave does not exist" return 0 endif String gName = GetWavesDataFolder(grains,2) Wave gVol=$(gName+"_Vol") Variable N=BinarySearch(gvol,minSize)+1 if (ItemsInList(GetRTStackInfo(0))<2) printf "from array '%s' there are %d grains of volume %d or bigger\r",NameOfWave(grains),N,minSize endif return N End // the contents of the supplied wave is index (i,j,k) not microns Function/S getGrainCoords(grains,grainNum) // create a wave containing the [ix][iy][iz] coords of each point in grain Wave grains // 3d wave with all of the grain numbers (e.g. gNeighbor) Variable grainNum // grain number if (!WaveExists(grains) || numtype(grainNum) || grainNum<0) String grainName=NameOfWave(grains) Prompt grainName,"3d wave with all grains",popup,ListMultiGrainMats() grainNum = numtype(grainNum)==2 ? 0 : max(grainNum,0) Prompt grainNum, "grain number" DoPrompt "choose grain",grainName,grainNum if (V_flag) return "" endif Wave grains = $grainName endif if (!WaveExists(grains)) return "" endif WaveStats/Q grains Variable maxGrain = V_max if (numtype(grainNum) || grainNum<0 || grainNum>maxGrain) return "" endif Variable Nx,Ny,Nz Nx=DimSize(grains,0) Ny=DimSize(grains,1) Nz=DimSize(grains,2) String str="xyz"+num2istr(grainNum) Make/N=(100,3)/O $str Wave xyz=$str Variable ix,iy,iz, N=0 for (ix=0;ix=DimSize(xyz,0)) Redimension/N=(DimSize(xyz,0)+100,3) xyz // extend xyz[][3] endif xyz[N][0] = ix xyz[N][1] = iy xyz[N][2] = iz N += 1 endif endfor endfor endfor Redimension/N=(N,3) xyz // extend xyz[][3] if (ItemsInList(GetRTStackInfo(0))<2) printf " for grain %d, made a list of the voxel indicies in '%s'\r",grainNum,GetWavesDataFolder(xyz,2) endif return GetWavesDataFolder(xyz,2) End Function grainCOMsingle(xyz) // returns the single index to the COM of the grain Wave xyz // xyz[N][3], a list of ix,iy,iz for each point in the grain if (!WaveExists(xyz)) String xyzName Prompt xyzName, "wave with gain points 'xyz*'",popup,WaveList("xyz*",";", "DIMS:2,MAXCOLS:3,MINCOLS:3") DoPrompt "grain list",xyzName if (V_flag) return NaN endif Wave xyz=$xyzName endif if (!WaveExists(xyz)) return NaN endif Wave XYZindex=:raw:XYZindex // 3d array with single index for each point Variable N=DimSize(xyz,0) Variable ix,iy,iz // results, must actually land on a point in xyz Make/N=(N)/O temp_find_avg temp_find_avg = xyz[p][0] ix = sum(temp_find_avg)/N temp_find_avg = xyz[p][1] iy = sum(temp_find_avg)/N temp_find_avg = xyz[p][2] iz = sum(temp_find_avg)/N KillWaves/Z temp_find_avg // now ix,iy,iz are the average values, next make sure that we have a real voxel Variable i,d2,min2=1e100,imin for (i=0;imaxGrain || i2>maxGrain) if (ItemsInList(GetRTStackInfo(0))<2) DoAlert 0,"invalid inputs" endif return NaN endif Wave g1 = $getGrainCoords(grains,i1) // list of grain coords for each grain Wave g2 = $getGrainCoords(grains,i2) Variable j1 = grainCOMsingle(g1) Variable j2 = grainCOMsingle(g2) Variable angle = angleBetweenPointsSymReduced(j1,j2) if (ItemsInList(GetRTStackInfo(0))<2) String degree = StrVarOrDefault("root:Packages:Grains:degree","¡") printf " angle between grains %d and %d is %g%s\r",i1,i2,angle,degree endif KillWaves/Z g1,g2 return angle End Function anglefromMatrix(index) // returns total rotaion angle (degree) // function to determine total rotation angle for one rotation matrix Variable index // index to point Wave OrientMat=:raw:OrientMat Variable trace trace = OrientMat[index][0][0]+OrientMat[index][1][1]+OrientMat[index][2][2] Variable cosine = (trace-1) / 2 cosine = min(max(-1,cosine),1) // ensure valid range for cosine return acos(cosine)*180/PI End Function/S ListMultiGrainMats() // creates a list of the matricies that contain all of the grains String list=WaveList("g*",";","DIMS:3") // next remove all items ending with "_number" from list String item,out="" Variable j,i=0 for (i=0;i traceMax) // save value for optimum rotation matrix traceMax = trace11[0] Si = symOp endif endfor KillWaves/Z symOp, trace11 End // // function to determine the angle between two rotation matricies // rotation matrix from a to b is just (Rb * Ra^-1), and don't forget that Ra^-1 = Rtranspose Function angleBetweenMatricies(mat1,mat2) // returns angle between 1 and 2 in degrees Wave mat1,mat2 MatrixOp/O temp_angle_11_ = Trace(mat2 x mat1^t) Variable cosine = (temp_angle_11_[0][0]-1) / 2 KillWaves/Z temp_angle_11_ cosine = min(max(-1,cosine),1) // ensure cosine in range [-1,1] return acos(cosine)*180/PI End // returns rot, rotation matrix from A to B, which is symmetry reduced to smallest total angle Static Function symReducedRotation(A,B,rot) Wave A,B // two orientation matricies Wave rot // resulting symmetry reduced rotation matrix String wName wName = UniqueName("SymMatrix",1,0) ; Make/N=(3,3)/D/O $wName ; Wave symOp = $wName wName = UniqueName("rotMat",1,0) ; Make/N=(3,3)/D/O $wName ; Wave BASi_ = $wName Wave symOps=$StrVarOrDefault("root:Packages:Grains:SymmetryOpsPath","") // wave with symmetry ops Variable N = NumberByKey("Nproper",note(symOps),"=") // number of symmetyry operations to check N = numtype(N) ? DimSize(symOps,0) : N Variable i, trace, traceMax = -4 for (i=0;i traceMax) // save value for optimum rotation matrix traceMax = trace rot = BASi_ endif endfor Variable cosine = (traceMax-1)/2 cosine = max(min(cosine,1),-1) Variable angle = acos(cosine)*180/PI KillWaves/Z symOp, BASi_ return angle End Function TransformSample2Beamline(vec) // change vec[3] from sample (xyz) -> beamline (XYZ) coordinates Wave vec Variable ys = vec[1], zs=vec[2] // save sample system y and z vec[1] = (-ys+zs)/sqrt(2) vec[2] = (-ys-zs)/sqrt(2) return 0 End Function TransformBeamline2Sample(vec) // change vec[3] from beamline (XYZ) -> sample (xyz) coordinates Wave vec Variable Ybl = vec[1], Zbl=vec[2] // save beamline system Y and Z vec[1] = (-Ybl-Zbl)/sqrt(2) vec[2] = ( Ybl-Zbl)/sqrt(2) return 0 End Function point_in_list(ix,iy,iz,list3,N) // returns true if (ix,iy,iz) is a valid point in list3 Variable ix,iy,iz Wave list3 // list3[N][3], list of triplets to check Variable N // number of points to check in list3 Variable i for (i=0;i-0.5) ci += i cj += j ck += k n += 1 endif endfor endfor endfor ci /= n cj /= n ck /= n End //Function test() // Variable i,j,k // GrainCOM($"",i,j,k) // print i,j,k //End //Function EditRefMatrix() : Table // if (WinType("ref_matrix_win")==2) // DoWindow /F ref_matrix_win // return 1 // endif // Edit/W=(127,45,437,170)/K=1 root:ref_matrix // Execute "ModifyTable width(Point)=42,format(root:ref_matrix)=3" // DoWindow/C ref_matrix_win //End Function/S grainRGBlist(i) Variable i i = mod(abs(i),7) switch(i) case 0: return "1,0,0" case 1: return "1,1,0" case 2: return "0,1,0" case 3: return "0,1,1" case 4: return "0,0,1" case 5: return "1,0,1" case 6: return "0.1,0.1,0.1" endswitch return "" End Function MakeWiderRainbow() Variable N=101 Make/N=(N,3)/O M_colors Variable i, frac Make/N=3/O color100_, color110_, color010_, color011_, color001_, color101_ color100_ = {1,0,0} // red color110_ = {1,1,0} // yellow color010_ = {0,1,0} // green color011_ = {0,1,1} // cyan color001_ = {0,0,1} // blue color101_ = {1,0,1} // magenta Variable step = (N-1)/5 for (i=0;i0 KillWaves/Z BadWaves_temp_ return bad End // here is the way to get rotation axis and angle. // From rotation matrices A, B, you get C=A*B^(-1) // The angle = acos((C11+C22+C33-1)/2) // cos(angle) = { Trace(A*Binverse)-1 } / 2 // axis direction (x,y,z): x=C23-C32, y=C31-C13, z=C12-C21 // Make sure to normalize A and B first. Function AngleBetweenMats24andSwaps(orient,ref,Sswap) Wave orient // matrix of all of the orientation matricies String Sswap // name of matrix to get the swap vector, if "" then don't make Sswap Wave ref // reference orientation matrix // From the orientation matricies, make swap vectors that will swap a*, b*, c* so that they are all // as close together as possible (less than 90¡) if (DimSize(orient,0)!=3 || DimSize(orient,1)!=3 || WaveDims(orient)!=2) Abort "MakeSwapVectorsReference, orient matrix is illegal size" endif if (!WaveExists(ref)) Abort "no refrence orientation matrix given" endif Make/N=3/O oneaxis__ Duplicate/O orient orient_Temp_Norm_ // a normalized version of orient (lattice vectors all length 1) oneaxis__ = orient[p][0] normalize(oneaxis__) orient_Temp_Norm_[][0] = oneaxis__[p] oneaxis__ = orient[p][1] normalize(oneaxis__) orient_Temp_Norm_[][1] = oneaxis__[p] oneaxis__ = orient[p][2] normalize(oneaxis__) orient_Temp_Norm_[][2] = oneaxis__[p] Duplicate/O ref ref_Temp_Norm_ // a normalized version of ref (lattice vectors all length 1) oneaxis__ = ref[p][0] normalize(oneaxis__) ref_Temp_Norm_[][0] = oneaxis__[p] oneaxis__ = ref[p][1] normalize(oneaxis__) ref_Temp_Norm_[][1] = oneaxis__[p] oneaxis__ = ref[p][2] normalize(oneaxis__) ref_Temp_Norm_[][2] = oneaxis__[p] MatrixTranspose ref_Temp_Norm_ Variable i,j,k,eps Variable i0=1,j0=2,k0=3 Make/N=(3,3)/O rmat__ Variable cosine Variable maxCos=-Inf for(k=-3;k<4;k+=1) // check all 48 of the 90¡ type rotations for(j=-3;j<4;j+=1) for(i=-3;i<4;i+=1) eps = epsilon_ijk(i,j,k) // one possible swapping if (eps==1) // only 24 right handed swappings are checked rmat__ = orient_Temp_Norm_ SwapXYZ2xyz(rmat__,i,j,k) // rmat__ is now swapped version of normalized orient MatrixMultiply rmat__,ref_Temp_Norm_ // M_product now contains product cosine = (MatrixTrace(M_product)-1)/2 // cosine of the rotation angle cosine = (cosine>1) ? (2-cosine) : cosine if (cosine>maxCos) maxCos = cosine i0=i ; j0=j ; k0=k endif endif endfor endfor endfor if (strlen(Sswap)>0) Make/N=3/O $Sswap Wave swaps = $Sswap swaps[0,2]={i0,j0,k0} // save best swap endif Killwaves/Z rmat__,M_product KillWaves/Z oneaxis__, ref_Temp_Norm_,orient_Temp_Norm_ return acos(maxCos)*180/PI // rotation angle in degrees End Function SwapXYZ2xyz(mat,x2,y2,z2) // permute axes so X->x, Y->y, Z->z Wave mat // matrix to permute (this is NOT a rotation) Variable x2,y2,z2 // destinations of x,y,z (use ±1, ±2, and ±3 (cannot use 0, since 0==±0) Variable eps = epsilon_ijk(x2,y2,z2) if (!eps) DoAlert 0, "SwapXYZ2xyz x2,y2,z2, must be a permutation of ±1,2, or 3" endif Make/N=(3,3)/O axses__ axses__ = mat mat[][0] = axses__[p][abs(x2)-1] * sign(x2) mat[][1] = axses__[p][abs(y2)-1] * sign(y2) mat[][2] = axses__[p][abs(z2)-1] * sign(z2) Killwaves/Z axses__ return eps End Function MakeRotationMatFromAB(a,b,rot) // makes rotation mat between a and b Wave a,b // input matricies Wave rot // rotation matrix // makes rmat, which is the rotation matricies between succesive grains // rmat[i] is the rotation needed to go from orient[i-1] to orient[i] if (numtype(a[0][0]) || numtype(b[0][0])) rot = NaN return 1 endif Make/N=(3,3)/O temp__, invA_temp__ rot = a temp__ = (p==q) MatrixLLS /O/Z rot temp__ // temp__ is now a^-1 invA_temp__ = temp__ // invA_temp__ is now a^-1 MatrixMultiply b,invA_temp__ // rotation matrix is now in M_product Wave M_product=M_product Variable det = MatrixDet(M_product) if (det>1) M_product /= det endif rot = M_product Killwaves/Z temp__,M_product, invA_temp__ return 0 EndMacro // ============================================================================ // ============== this section are matrix math routines from the old PoleFigure.ipf =============== Function rotationAngleOfMat(rot) // returns the total rotation angle of a matrix 'rot' Wave rot // the rotation matrix Variable trace = MatrixTrace(rot) // trace = 1 + 2*cos(theta) Variable cosine = (trace-1)/2 // cosine of the rotation angle cosine = (cosine>1) ? (2-cosine) : cosine return acos(cosine)*180/PI // rotation angle in degrees End Function rotationCosineOfMat(rot) // returns cos(total rotation angle of matrix) for matrix 'rot' Wave rot Variable trace = MatrixTrace(rot) // trace = 1 + 2*cos(theta) return (trace-1)/2 // cosine of the rotation angle End Function axisOfMatrix(rot,axis) // compute angle and axis of a rotation matrix // returns total rotation angle, and sets axis to the axis of the total rotation Wave rot // rotation matrix Wave axis // axis of the rotation (angle is returned) Make/N=(3,3)/O/D axisMat__ axis = rot[p][0] normalize(axis) axisMat__[][0] = axis[p] axis = rot[p][1] normalize(axis) axisMat__[][1] = axis[p] axis = rot[p][2] normalize(axis) axisMat__[][2] = axis[p] Variable cosine = (MatrixTrace(axisMat__)-1)/2 // trace = 1 + 2*cos(theta) cosine = max(-1,min(cosine,1)) if (cosine<= -1) // special for 180¡ rotation, axis[0] = sqrt((axisMat__[0][0]+1)/2) axis[1] = sqrt((axisMat__[1][1]+1)/2) axis[2] = sqrt((axisMat__[2][2]+1)/2) // always assume z positive axis[0] = (axisMat__[0][2]+axisMat__[2][0])<0 ? -axis[0] : axis[0] axis[1] = (axisMat__[1][2]+axisMat__[2][1])<0 ? -axis[1] : axis[1] else // rotaion < 180¡, usual formula works axis[0] = axisMat__[1][2] - axisMat__[2][1] axis[1] = axisMat__[2][0] - axisMat__[0][2] axis[2] = axisMat__[0][1] - axisMat__[1][0] endif normalize(axis) KillWaves /Z axisMat__ return acos(cosine)*180/PI // rotation angle in degrees End Function rotationMatAboutAxis(axis,angle,mat) Wave axis // axis about which to rotate Variable angle // angle to rotate (passed as degrees, but convert to radians) Wave mat // desired rotation matrix angle *= PI/180 Make/N=3/O/D xhat_rotate__, yhat_rotate__, zhat_rotate__ Wave xhat=xhat_rotate__ Wave yhat=yhat_rotate__ Wave zhat=zhat_rotate__ zhat = axis if (normalize(zhat) <=0) KillWaves/Z xhat_rotate__, yhat_rotate__, zhat_rotate__ return 1 // error, cannot rotate about a zero length axis endif Variable i i = ( abs(zhat[0])<= abs(zhat[1]) ) ? 0 : 1 i = ( abs(zhat[2])< abs(zhat[i]) ) ? 2 : i xhat = zhat xhat[i] = 2 // choose x-z plane normalize(xhat) Variable dotxz = MatrixDot(xhat,zhat) xhat -= dotxz*zhat // xhat is now perpendicular to zhat normalize(xhat) // xhat is now normalized vector perp to zhat crossV4(zhat,xhat,yhat) normalize(yhat) // yhat is now normalized vector perp to zhat and xhat // xhat, yhat, zhat is now an orthonormal system with zhat || to axis // now rotate around zhat in the xhat -> yhat direction, an angle of angle Variable cosa = cos(angle) Variable sina = sin(angle) Redimension/N=(3,3) mat for(i=0;i<3;i+=1) mat[][i] = (xhat[i]*cosa+yhat[i]*sina)*xhat[p] + (-xhat[i]*sina + yhat[i]*cosa)*yhat[p] + zhat[i]*zhat[p] endfor KillWaves/Z xhat_rotate__, yhat_rotate__, zhat_rotate__ End // cross supplanted by the Cross operation in Igor 5 Static Function crossV4(a,b,c) // vector cross product Wave a,b,c c[2] = a[0]*b[1] - a[1]*b[0] c[0] = a[1]*b[2] - a[2]*b[1] c[1] = a[2]*b[0] - a[0]*b[2] End Function rotation(mat,axis,angle) // rotate mat about x, y, or z by angle Wave mat // matrix to rotate Variable axis // axis to rotate about 0=x, 1=y, 2=z Variable angle // angle to rotate (radians) Variable cosa = cos(angle) Variable sina = sin(angle) Make/N=(3,3)/O mat_rot_ mat_rot_ = (p==q) switch(axis) case 0: // rotate about x-axis mat_rot_[1][1] = cosa mat_rot_[2][2] = cosa mat_rot_[1][2] = -sina mat_rot_[2][1] = sina break case 1: // rotate about y-axis mat_rot_[0][0] = cosa mat_rot_[2][2] = cosa mat_rot_[0][2] = sina mat_rot_[2][0] = -sina break case 2: // rotate about z-axis mat_rot_[0][0] = cosa mat_rot_[1][1] = cosa mat_rot_[0][1] = -sina mat_rot_[1][0] = sina break default: endswitch Killwaves/Z mat_rot_ End // here is the way to get rotation axis and angle. // From rotation matrices A, B, you get C=A*B^(-1) // The angle = acos((C11+C22+C33-1)/2) // cos(angle) = { Trace(A*Binverse)-1 } / 2 // axis direction (x,y,z): x=C23-C32, y=C31-C13, z=C12-C21 // Make sure to normalize A and B first. // Wenge Function AngleBetweenMats(am,bm) Wave am,bm // the two matricies Make/N=(3,3)/O am__, bm__ Make/N=3/O oneaxis__ oneaxis__ = am[p][0] normalize(oneaxis__) am__[][0] = oneaxis__[p] oneaxis__ = am[p][1] normalize(oneaxis__) am__[][1] = oneaxis__[p] oneaxis__ = am[p][2] normalize(oneaxis__) am__[][2] = oneaxis__[p] oneaxis__ = bm[p][0] normalize(oneaxis__) bm__[][0] = oneaxis__[p] oneaxis__ = bm[p][1] normalize(oneaxis__) bm__[][1] = oneaxis__[p] oneaxis__ = bm[p][2] normalize(oneaxis__) bm__[][2] = oneaxis__[p] MatrixTranspose bm__ MatrixMultiply am__,bm__ // M_product now contain product KillWaves/Z am__, bm__, oneaxis__ return rotationAngleOfMat(M_product) // return acos((MatrixTrace(M_product)-1)/2)*180/PI End Static Function normalize(a) // normalize a and return the initial magnitude Wave a if (WaveDims(a)==1) Variable norm_a = norm(a) if (norm_a==0) return 0 endif a /= norm_a return norm_a elseif(WaveDims(a)==2 && DimSize(a,0)==DimSize(a,1)) Variable det = MatrixDet(a) a /= det return det endif End Function epsilon_ijk(i,j,k) Variable i,j,k // returns +1 for even permutaion, -1 for odd permutaions Variable ps = sign(i)*sign(j)*sign(k) i = round(abs(i)) j = round(abs(j)) k = round(abs(k)) if (i*j*k ==0) // no zeros allowed return 0 endif if (i>3 || j>3 || k>3) // must be in range [1,3] return 0 endif if (i==j || j==k || k==i) // do duplicates return 0 endif Variable perm = (i==1) + 2*(j==2) + 4*(k==3) perm = ((perm==0) || (perm==7)) ? 1 : -1 return ps*perm End //Function test48() // Variable i,j,k,eps,n=1 // Variable pos=0,neg=0 // for(k=-3;k<4;k+=1) // for(j=-3;j<4;j+=1) // for(i=-3;i<4;i+=1) // eps = epsilon_ijk(i,j,k) // if (eps) // printf "%3d %+d, %+d, %+d %+d\r",n,i,j,k,eps // pos = (eps>0) ? pos+1 : pos // neg = (eps>0) ? neg+1 : neg // n += 1 // endif // endfor // endfor // endfor // print "pos = ",pos," neg=",neg //End // ========================================================================= // ========================================================================= // ========================================================================= Function/S GetGizmoNote() Execute "GetGizmo gizmoName" // check if a gizmo is up SVAR S_GizmoName=S_GizmoName if (strlen(S_GizmoName)<1) return "" endif Execute "GetGizmo objectList" Wave/T TW_gizmoObjectList=TW_gizmoObjectList String str Variable i, i2=0, i1=2 for (i=0;i=0) Abort "this note trick for Gizmos does not work with semi-colons" endif Execute "GetGizmo gizmoName" // check if a gizmo is up SVAR S_GizmoName=S_GizmoName if (strlen(S_GizmoName)<1) return 1 endif Execute "GetGizmo objectList" SVAR S_gizmoObjectList=S_gizmoObjectList Variable i String cmd if (!(stringmatch(S_gizmoObjectList,"*AppendToGizmo string=*,name=GizmoNoteString*"))) sprintf cmd, "AppendToGizmo string=\"%s\",strFont=\"\",name=GizmoNoteString",noteStr else sprintf cmd, "ModifyGizmo modifyObject=GizmoNoteString, property={string,\"%s\"}",noteStr endif Execute cmd KillWaves/Z TW_gizmoObjectList KillStrings/Z S_gizmoObjectList End Function initGrainGizmo() Variable doit = 0 doit = (exists("root:Packages:Grains:explode")!=2) ? 1 : doit doit = (exists("root:Packages:Grains:Xcursor")!=2) ? 1 : doit doit = (exists("root:Packages:Grains:Ycursor")!=2) ? 1 : doit doit = (exists("root:Packages:Grains:Zcursor")!=2) ? 1 : doit doit = (exists("root:Packages:Grains:cursorGrainNum")!=2) ? 1 : doit doit = (exists("root:Packages:Grains:radiusBndry")!=2) ? 1 : doit doit = (exists("root:Packages:Grains:SymmetryOpsPath")!=2) ? 1 : doit doit = (exists("root:Packages:Grains:CubicSymmetryOps")!=1) ? 1 : doit doit = (exists("root:Packages:Grains:sigmaCSL")!=1) ? 1 : doit // always set micorn and degree to make it easier to move an experiment back and forth between Mac and Windows String/G root:Packages:Grains:micron=SelectString(stringmatch(igorInfo(2),"Macintosh"),"micron","µm") String/G root:Packages:Grains:degree=SelectString(stringmatch(igorInfo(2),"Macintosh"),"degree","¡") if (!doit) return 0 endif NewDataFolder/O root:Packages NewDataFolder/O root:Packages:Grains Variable explode = NumVarOrDefault("root:Packages:Grains:explode",0) Variable Xcursor = NumVarOrDefault("root:Packages:Grains:Xcursor",0) Variable Ycursor = NumVarOrDefault("root:Packages:Grains:Ycursor",0) Variable Zcursor = NumVarOrDefault("root:Packages:Grains:Zcursor",0) Variable cursorGrainNum = NumVarOrDefault("root:Packages:Grains:cursorGrainNum",NaN) Variable radiusBndry = NumVarOrDefault("root:Packages:Grains:radiusBndry",Inf) String SymmetryOpsPath = StrVarOrDefault("root:Packages:Grains:SymmetryOpsPath","root:Packages:Grains:CubicSymmetryOps") Variable/G root:Packages:Grains:explode=explode Variable/G root:Packages:Grains:Xcursor=Xcursor Variable/G root:Packages:Grains:Ycursor=Ycursor Variable/G root:Packages:Grains:Zcursor=Zcursor Variable/G root:Packages:Grains:cursorGrainNum=cursorGrainNum Variable/G root:Packages:Grains:radiusBndry=radiusBndry String/G root:Packages:Grains:SymmetryOpsPath=SymmetryOpsPath MakeCubicSymmetryOps("root:Packages:Grains:") Make_sigmaCSL("root:Packages:Grains:") End Function Make_sigmaCSL(fldr) // make a wave with data on Sigma boundaries for Cubic CSL // columns for sigmaCSL are "Sigma, angle¡, h,k,l String fldr // folder used to put CubicSymmetryOps String wName = fldr+"sigmaCSL" Make/N=(1,1)/O $wName Wave sigmaCSL=$wName // This table from H. Mykura, H. (1980). Grain-Boundary Structure and Kinetics, pp. 445-456. Metals Park: American Society for Metals. // Faxed to me by Budai Apr. 4, 2005 sigmaCSL[0][0]= {3,5,7,9,11,13.1,13.2,15,17.1,17.2,19.1,19.2,21.1,21.2,23,25.1,25.2,27.1,27.2,29.1,29.2,31.1,31.2,33.1,33.2,33.3,35.1,35.2,37.1,37.2,37.3,39.1,39.2,41.1,41.2,41.3,43.1,43.2,43.3,45.1,45.2} sigmaCSL[41][0]= {45.3,47.1,47.2,49.1,49.2,49.3} sigmaCSL[0][1]= {60,36.87,38.21,38.94,50.48,22.62,27.8,48.19,28.07,61.93,26.53,46.83,21.79,44.4,40.45,16.25,51.68,31.58,35.42,43.6,46.39,17.9,52.19,20.05,33.55,58.98,34.04,43.23,18.92,43.13,50.57,32.21} sigmaCSL[32][1]= {50.13,12.68,40.88,55.88,15.18,27.91,60.77,28.62,36.87,53.13,37.07,43.66,43.58,43.58,49.22} sigmaCSL[0][2]= {1,0,1,0,0,0,1,0,0,1,0,1,1,1,1,0,1,0,0,0,1,1,1,0,1,0,1,1,0,0,1,1,1,0,0,0,1,0,2,1,1,1,1,0,1,1,2} sigmaCSL[0][3]= {1,0,1,1,1,0,1,1,0,2,1,1,1,1,1,0,3,1,1,0,2,1,1,1,1,1,1,3,0,1,1,1,2,0,1,1,1,1,3,1,2,2,3,2,1,1,2} sigmaCSL[0][4]= {1,1,1,1,1,1,1,2,1,2,1,1,1,2,3,1,3,1,2,1,2,1,2,1,3,1,2,3,1,3,1,1,3,1,2,1,1,2,3,3,2,2,3,3,1,5,3} wName = fldr+"hkl_temp__" // do this section to make sure that hkl are ordered properly (123) is OK, not (3,2,1) Make/N=3/O $wName Wave hkl=$wName Variable i for (i=0;i1) printAll = (printAll>0) ? 2 : 1 Prompt printAll, "print all the symmetry matrices?", popup, "only errors;print matrices" DoPrompt "print all",printAll if (V_flag) return 1 endif printAll -= 1 endif Wave cubicOps = root:Packages:Grains:CubicSymmetryOps Make/N=(3,3)/O/D op,opj,tempSum33 Variable i,j,N=DimSize(cubicOps,0) Variable det,sumAbs,err=0 for (i=0;i1e-12 && abs(det+1)>1e-12) || abs(sumAbs-3)>1e-12) printf "checking op %d, Det(op)=%g, Sum|op[i][j]|=%g\r",i,det,sumAbs print3x3(op) err += 1 endif for (j=i+1;j0) printf "fournd %d errors\r",err else printf "no errors found\r" endif KIllWaves/Z op,opj,tempSum33,temp33 End Static Function printSymmetryMatrix(i) Variable i // index, of which cubic matrix to print i in [0,47] Wave symOps=$StrVarOrDefault("root:Packages:Grains:SymmetryOpsPath","") // wave with symmetry ops Make/N=(3,3)/O/D temp_printSymmetryMatrix Wave op = temp_printSymmetryMatrix op = symOps[i][p][q] Make/N=3/O/D temp_axis_ Wave axis = temp_axis_ Variable angle = axisOfMatrix(op,axis) integerDirectionApprox(axis,12) Variable det = MatrixDet(op) Variable a0=op[0][0], a1=op[1][1], a2=op[2][2] // elements on trace String str if (det==1 && angle==0) str = "Identity matrix" else String degree = StrVarOrDefault("root:Packages:Grains:degree","¡") sprintf str, "a %g%s rotation of about the (%g, %g, %g)",angle,axis[0],axis[1],axis[2] endif printf "symOp[%02d] =\t%+d, %+d, %+d %s\r",i,symOps[i][0][0],symOps[i][0][1],symOps[i][0][2],str printf "\t\t\t\t%+d, %+d, %+d\r",symOps[i][1][0],symOps[i][1][1],symOps[i][1][2] printf "\t\t\t\t%+d, %+d, %+d\r",symOps[i][2][0],symOps[i][2][1],symOps[i][2][2] KillWaves/Z temp_printSymmetryMatrix,temp_axis_ return angle End Static Function print3x3(mat3x3) Wave mat3x3 printf "%s = \r",NameOfwave(mat3x3) printf "\t%+.4f, %+.4f, %+.4f\r",mat3x3[0][0],mat3x3[0][1],mat3x3[0][2] printf "\t%+.4f, %+.4f, %+.4f\r",mat3x3[1][0],mat3x3[1][1],mat3x3[1][2] printf "\t%+.4f, %+.4f, %+.4f\r",mat3x3[2][0],mat3x3[2][1],mat3x3[2][2] End