#pragma rtGlobals=3 // Use modern global access method and strict wave access. #pragma moduleName=CubeHelix #include , version >= 6.2 // for WMGetRECREATIONInfoByKey // Creates and applies a "Cube Helix" color index wave to Igor image plots. // // Inspired by http://www.mrao.cam.ac.uk/~dag/CUBEHELIX/cubetry.html // // See "Dave Green's `cubehelix' colour scheme" here: http://www.mrao.cam.ac.uk/~dag/CUBEHELIX // // Written by Jim Prouty, WaveMetrics, Inc. StrConstant ksModuleName= "CubeHelix" StrConstant ksCubeHelixGraph="CubeHelixDesigner" StrConstant ksCubeHelixPanel="CubeHelixDesigner#PTOP" StrConstant ksCubeHelixColorIndexWave = "root:cubehelix" StrConstant ksCubeHelixDemoImageWave = "root:cubeHelixDemoImage" StrConstant ksNoneItem= "\\M0:(:_none_" Menu "Graph" "Cube Helix Color Index", /Q, ShowCubeHelixDesigner() End Function ShowCubeHelixDesigner() Variable n= 100, start= 0.5, rots= 1.5, rotationDirection=-1, hue=1.25, theGamma = 1, reversed=0 // DEFAULTS, should match values set to controls, below. WAVE cubehelix= MakeCubeHelixCTab(n, start, rots, rotationDirection, hue, thegamma, reversed, "cubehelix") String traceName= NameOfWave(cubehelix) Make/O/N=(256,2) $ksCubeHelixDemoImageWave/WAVE=image // we'll delete this when the panel closes. image= p String imageName= NameOfWave(image) DoWindow/K $ksCubeHelixGraph Display/K=1/W=(224,61,777,589)/N=$ksCubeHelixGraph/R cubehelix[*][0],cubehelix[*][1],cubehelix[*][2] as "Cube Helix Designer" AppendImage/T $ksCubeHelixDemoImageWave ModifyImage $imageName cindex= cubehelix ModifyGraph margin=14 ModifyGraph lSize=2 ModifyGraph rgb($traceName#1)=(3,52428,1),rgb($traceName#2)=(0,0,65535) ModifyGraph mirror=0, nticks=0,standoff=0 Label right "\\u#2" SetAxis/A/R left ControlBar 226 NewPanel/W=(0.2,0.2,0.8,0.016)/FG=(FL,FT,GR,)/HOST=# SetVariable helixStart,pos={85,17},size={131,19},bodyWidth=60,proc=CubeHelix#CubeHelixSetVarProc,title="Start Color" SetVariable helixStart,limits={0,3,0.5},value= _NUM:0.5 SetVariable helixRots,pos={21,51},size={195,19},bodyWidth=60,proc=CubeHelix#CubeHelixSetVarProc,title="Number of Rotations" SetVariable helixRots,limits={0,3,0.5},value= _NUM:1.5 CheckBox helixDirection,pos={232,53},size={188,16},proc=CubeHelix#HelixRotateCheckProc,title="Rotate in Positive Direction" CheckBox helixDirection,value= 0 SetVariable helixHue,pos={127,85},size={89,19},bodyWidth=60,proc=CubeHelix#CubeHelixSetVarProc,title="Hue" SetVariable helixHue,limits={0,2,0.25},value= _NUM:1.25 SetVariable helixGamma,pos={104,117},size={112,19},bodyWidth=60,proc=CubeHelix#CubeHelixSetVarProc,title="Gamma" SetVariable helixGamma,limits={0.2,2,0.2},value= _NUM:1 SetVariable helixN,pos={39,151},size={176,19},bodyWidth=60,proc=CubeHelix#CubeHelixSetVarProc,title="Number of Colors" SetVariable helixN,limits={10,1000,10},value= _NUM:100 CheckBox reversed,pos={137,191},size={75,16},proc=CubeHelix#HelixReversedCheckProc,title="Reversed",value= 0 GroupBox apply,pos={234,86},size={265,133},title="Apply Cube Helix Color Index to" PopupMenu graph,pos={247,113},size={113,20},proc=CubeHelix#CubeHelixGraphPopMenuProc,title="Graph" PopupMenu graph,mode=1,value= #"CubeHelix#GraphsWithImagesList()" TitleBox graphTItle,pos={297,138},size={69,13},title="title here",fSize=11 TitleBox graphTItle,frame=0,fColor=(0,0,65535) PopupMenu image,pos={252,158},size={99,20},proc=CubeHelix#CubeHelixGraphPopMenuProc,title="Image" PopupMenu image,mode=1,value= #"CubeHelix#ImagesInSelectedGraph()" Button applyOrUpdate,pos={399,187},size={68,20},proc=CubeHelix#HelixApplyButtonProc,title="Update" Button applyOrUpdate,userdata(title)= "Remove" Button remove,pos={292,187},size={68,20},proc=CubeHelix#HelixRemoveButtonProc,title="Remove" RenameWindow #,PTop SetActiveSubwindow ## ApplyColorIndexWaveToImage(ksCubeHelixGraph, "", cubehelix) CubeHelixUpdateForWindows() SetWindow $ksCubeHelixGraph hook(CubeHelix)= CubeHelixWindowHook End Function/WAVE MakeCubeHelixCTab(n, start, rots, rotationDirection, hue, thegamma, reversed, colorIndexWaveName) Variable n, start, rots, rotationDirection, hue, thegamma,reversed String colorIndexWaveName Make/O/N=(n,3) $colorIndexWaveName/WAVE=cubehelix // mark this wave as a cube helix wave. // We could also use this info to set the Cube Helix Designer controls from an existing graph. String str sprintf str, "cubeHelix;start:%g;rots=%g;rotationDirection=%d;hue=%g;gamma=%g;reversed=%d;", start, rots, rotationDirection, hue, thegamma,reversed Note/K cubehelix, str rots *= rotationDirection; Variable i for (i = 0; i < n; i+=1) Variable fract = i/(n-1) if( reversed ) fract = 1-fract endif Variable red, grn, blu CubeHelixRGB(fract,start,rots,hue,thegamma,red, grn, blu) // limited to 0-1 cubehelix[i][0] = 65535*red cubehelix[i][1]= 65535*grn cubehelix[i][2] = 65535*blu endfor return cubehelix End static Function CubeHelixRGB(fract,start,rots,hue,thegamma,red, grn, blu) Variable fract,start,rots,hue,thegamma // inputs Variable &red, &grn, &blu // outputs, 0-1 Variable angle = 2*pi*(start/3.0+1+rots*fract) Variable amp=hue*fract*(1-fract)/2.0; fract = fract ^ thegamma red=fract+amp*(-0.14861*cos(angle)+1.78277*sin(angle)) grn=fract+amp*(-0.29227*cos(angle)-0.90649*sin(angle)) blu=fract+amp*(+1.97294*cos(angle)) red = limit(red,0,1) grn = limit(grn,0,1) blu = limit(blu,0,1) End Function CubeHelixWindowHook(s) STRUCT WMWinHookStruct &s Variable hookResult = 0 strswitch(s.eventName) case "activate": CubeHelixUpdateForWindows() break case "kill": Execute/Q/P "KillWaves/Z "+ksCubeHelixColorIndexWave+", "+ksCubeHelixDemoImageWave break endswitch return hookResult End // creates the cubehelix wave and demonstrates it in the Cube Helix Designer. static Function UpdateCubeHelix() // get all the params from the controls Variable n, start, rots, rotationDirection, hue, thegamma, reversed String win= ksCubeHelixPanel ControlInfo/W=$win helixStart start= V_Value ControlInfo/W=$win helixRots rots= V_Value ControlInfo/W=$win helixDirection rotationDirection= V_Value ? 1 : -1 ControlInfo/W=$win helixHue hue= V_Value ControlInfo/W= $win helixGamma thegamma= V_Value ControlInfo/W=$win helixN n= V_Value ControlInfo/W=$win reversed reversed = V_Value WAVE colorIndex= MakeCubeHelixCTab(n, start, rots, rotationDirection, hue, thegamma, reversed, ksCubeHelixColorIndexWave) ApplyColorIndexWaveToImage(ksCubeHelixGraph, "", colorIndex) End static Function HelixRemoveButtonProc(ctrlName) : ButtonControl String ctrlName String graphName= HelixSelectedGraph() // "" if ksNoneItem String imageName= HelixSelectedImage() // "" if ksNoneItem if( strlen(graphName) && strlen(imageName) ) WAVE/Z helix= GetCubeHelixColorIndexWave(graphName,imageName) //ModifyImage/W=$graphName $imageName, cindex=$"" NOPE ModifyImage/W=$graphName $imageName, ctab={*,*,Grays} if( WaveExists(helix) ) KillWaves/Z helix endif CubeHelixUpdateForWindows() // mostly to update the Apply/Remove button. endif End static Function HelixApplyButtonProc(ctrlName) : ButtonControl String ctrlName String graphName= HelixSelectedGraph() // "" if ksNoneItem String imageName= HelixSelectedImage() // "" if ksNoneItem if( strlen(graphName) && strlen(imageName) ) WAVE/Z prototypeWave= $ksCubeHelixColorIndexWave if( WaveExists(prototypeWave) ) // make a wave name based on the wave's full path String perImageCubeHelixName= CubeHelixWaveNameForImage(graphName, imageName) Duplicate/O prototypeWave, $perImageCubeHelixName WAVE perImageCubeHelixColorIndex = $perImageCubeHelixName ApplyColorIndexWaveToImage(graphName, imageName, perImageCubeHelixColorIndex) else Beep endif CubeHelixUpdateForWindows() // mostly to update the Apply/Remove button. endif End static Function ApplyColorIndexWaveToImage(graphName, imageName, colorIndexWave) String graphName // can be "" for top graph String imageName // can be "" for first image WAVE/Z colorIndexWave // get the top image's Z range. if( strlen(imageName) == 0 ) imageName= StringFromList(0,ImageNameList(graphName,";")) endif Wave/Z image= ImageNameToWaveRef(graphName,imageName) if( waveExists(image) ) WaveStats/Q/M=0 image // apply to the top image SetScale/I x, V_Min, V_Max, "", colorIndexWave ModifyImage/W=$graphName $imageName, cindex= colorIndexWave endif End static Function/S CubeHelixWaveNameForImage(graphName, imageName) String graphName, imageName String name= "cubeHelixCIndex" WAVE/Z image= ImageNameToWaveRef(graphName, imageName) if( WaveExists(image) ) String path= GetWavesDataFolder(image,2) String hashStr= Hash(path, 1) name=("cubeHelix"+hashStr)[0,30] endif return name End static Function CubeHelixGraphPopMenuProc(ctrlName,popNum,popStr) : PopupMenuControl String ctrlName Variable popNum String popStr CubeHelixUpdateForWindows() End static Function AfterWindowCreatedHook(createdWin, createdWinType) String createdWin Variable createdWinType CubeHelixUpdateForWindows() End static Function CubeHelixSetVarProc(ctrlName,varNum,varStr,varName) : SetVariableControl String ctrlName Variable varNum String varStr String varName UpdateCubeHelix() End static Function HelixRotateCheckProc(ctrlName,checked) : CheckBoxControl String ctrlName Variable checked UpdateCubeHelix() End static Function HelixReversedCheckProc(ctrlName,checked) : CheckBoxControl String ctrlName Variable checked UpdateCubeHelix() End // turns "\\M0:(:_none_" into "_none_" static Function/S RemoveEscapes(str) String str if( CmpStr(str, ksNoneItem) == 0 ) str= "_none_" endif return str End Static Function IsNone(str) String str Variable isNone= CmpStr(str, ksNoneItem) == 0 || CmpStr(str, "_none_") == 0 return isNone End static Function CubeHelixUpdateForWindows() GetWindow/Z $ksCubeHelixPanel wtitle if( V_Flag != 0 ) return -1 endif Variable buttonDisable= 0 // that is, enabled. // sync graph popup String graphs= GraphsWithImagesList() String graphName= HelixSelectedGraph() Variable mode= 1 // first graph listed if( strlen(graphName) ) mode= 1+WhichListItem(graphName, graphs) if( mode < 1 ) // selected graph was killed. mode = 1 graphName= StringFromList(0,graphs) endif else graphName= StringFromList(0,graphs) // can be ksNoneItem endif String popvalue = RemoveEscapes(graphName) PopupMenu graph, win=$ksCubeHelixPanel, popvalue= popvalue, mode=mode if( CmpStr(graphName, ksNoneItem) == 0 ) buttonDisable= 2 // visible disabled TitleBox graphTItle, win=$ksCubeHelixPanel, disable=2 // hide window title else GetWindow $graphName, wtitle TitleBox graphTItle, win=$ksCubeHelixPanel, disable=0, title=S_value endif // sync image popup String imageName= HelixSelectedImage() // "" if ksNoneItem if( (strlen(graphName) > 0) && (CmpStr(graphname, ksNoneItem) != 0) ) String images= ImageNameList(graphName,";") mode= 1+WhichListItem(imageName, images) if( mode < 1 ) // selected graph was killed. mode = 1 imageName= StringFromList(0,images) endif else mode= 1 imageName= ksNoneItem endif popvalue = RemoveEscapes(imageName) PopupMenu image, win=$ksCubeHelixPanel, popvalue= popvalue, mode=mode if( CmpStr(imageName, ksNoneItem) == 0 ) buttonDisable= 2 // visible disabled endif // enable/re-title the apply buttons String title="Apply" Variable removeDisable = 2 // disabled if( buttonDisable == 0 ) // we have a graph and an image WAVE/Z helix= GetCubeHelixColorIndexWave(graphName,imageName) if( WaveExists(helix) ) title= "Update" removeDisable = 0 // shown endif endif Button applyOrUpdate, win=$ksCubeHelixPanel, title=title, disable=buttonDisable, userdata(title)=title Button remove, win=$ksCubeHelixPanel, disable=removeDisable // TO DO: // If imageAlreadyHasCubeHelix, set control to the settings stored in the cube helix wave. End static Function/WAVE GetCubeHelixColorIndexWave(graphName,imageName) String graphName,imageName WAVE/Z cindex= $GetImageColorIndexWave(graphName,imageName) if( WaveExists(cindex) ) Variable imageAlreadyHasCubeHelix = IsCubeHelixWave(cindex) if( imageAlreadyHasCubeHelix ) return cindex endif endif return $"" End // Returns the full path to the image's cindex wave or "" if not specified or if the wave no longer exists. static Function/S GetImageColorIndexWave(graphName,imageName) String graphName, imageName String info = ImageInfo(graphName, imageName, 0) if( strlen(info) ) String relativePath= WMGetRECREATIONInfoByKey("cindex",info) relativePath= RemoveLeadingSpaces(relativePath) // Now we have a relative path from root data folder, either a bare wave name, // or something like :subfolder:luWave. String fullPath= "root:"+relativePath Wave/Z lookup= $fullPath if( WaveExists(lookup) ) info= GetWavesDataFolder(lookup,2) // possibly quoted, else info="" endif endif return info End static Function/S RemoveLeadingSpaces(str) String str Variable i, n= strlen(str) for(i=0; i= 0 End