#pragma rtGlobals=3 #pragma version=1.0 #pragma IgorVersion=8.0 // ============================================ // Trace Opacity Editor // A tool to change opacity (alpha) of multiple // selected traces while preserving their RGB colors // ============================================ Menu "Graph" "Trace Opacity Editor...", /Q, ShowTraceOpacityPanel() End // ============================================ // Main Panel Function // ============================================ Function ShowTraceOpacityPanel() String gname = WinName(0, 1) if (strlen(gname) == 0) DoAlert 0, "No graph window found. Please create a graph first." return 0 endif // Create data folder for panel globals NewDataFolder/O root:Packages NewDataFolder/O root:Packages:TraceOpacityEditor DFREF dfr = root:Packages:TraceOpacityEditor // Store current graph name String/G dfr:gCurrentGraph = gname Variable/G dfr:gCurrentAlpha = 65535 // Build trace list wave for the ListBox BuildTraceListWave(gname) // Create panel (kill existing first) DoWindow/K TraceOpacityPanel NewPanel/K=1/W=(100,100,480,610) as "Trace Opacity Editor" DoWindow/C TraceOpacityPanel // Graph selector TitleBox graphLabel, pos={10,12}, size={50,20}, title="Graph:", frame=0 PopupMenu graphPopup, pos={60,10}, size={250,20} PopupMenu graphPopup, value=#"WinList(\"*\",\";\",\"WIN:1\")", proc=GraphPopupProc PopupMenu graphPopup, popvalue=gname // Refresh button Button refreshBtn, pos={320,8}, size={50,22}, title="Refresh", proc=RefreshListProc // ListBox for trace selection (multi-select enabled) TitleBox listLabel, pos={10,40}, size={100,20}, title="Select traces:", frame=0 ListBox traceListBox, pos={10,60}, size={360,280} ListBox traceListBox, listWave=dfr:wTraceNames, selWave=dfr:wTraceSelection ListBox traceListBox, mode=4, proc=TraceListBoxProc // Multiple selection with shift/ctrl ListBox traceListBox, widths={200,80} // Selection buttons Button selectAllBtn, pos={10,345}, size={85,22}, title="Select All", proc=SelectAllProc Button selectNoneBtn, pos={100,345}, size={85,22}, title="Select None", proc=SelectNoneProc Button invertSelBtn, pos={190,345}, size={85,22}, title="Invert", proc=InvertSelectionProc // Alpha controls GroupBox alphaGroup, pos={10,375}, size={360,90}, title="Opacity (Alpha)" TitleBox alphaMinLabel, pos={20,400}, size={20,15}, title="0", frame=0 Slider alphaSlider, pos={40,398}, size={200,20} Slider alphaSlider, limits={0,65535,1}, value=65535, vert=0 Slider alphaSlider, proc=AlphaSliderProc, ticks=0 TitleBox alphaMaxLabel, pos={245,400}, size={40,15}, title="65535", frame=0 SetVariable alphaValue, pos={290,398}, size={70,20}, title=" " SetVariable alphaValue, value=dfr:gCurrentAlpha, limits={0,65535,1} SetVariable alphaValue, proc=AlphaSetVarProc TitleBox alphaPercent, pos={20,435}, size={200,15}, title="(0=transparent, 65535=opaque)", frame=0, fSize=10 // Live preview checkbox CheckBox livePreviewCheck, pos={290,435}, size={80,15}, title="Live Preview" CheckBox livePreviewCheck, value=0, proc=LivePreviewCheckProc // Action buttons Button applyBtn, pos={10,475}, size={110,28}, title="Apply to Selected", proc=ApplyAlphaProc Button applyBtn, fColor=(0,52224,0) Button undoBtn, pos={130,475}, size={70,28}, title="Undo", proc=UndoAlphaProc Button closeBtn, pos={210,475}, size={70,28}, title="Close", proc=ClosePanelProc return 0 End // ============================================ // Build waves for ListBox display // ============================================ Function BuildTraceListWave(String gname) DFREF dfr = root:Packages:TraceOpacityEditor String traces = TraceNameList(gname, ";", 1) Variable ntraces = ItemsInList(traces) if (ntraces == 0) Make/O/T/N=1 dfr:wTraceNames = {"(no traces)"} Make/O/N=1 dfr:wTraceSelection = 0 Make/O/N=(1,4) dfr:wOriginalColors = 0 return 0 endif Make/O/T/N=(ntraces,2) dfr:wTraceNames Make/O/N=(ntraces) dfr:wTraceSelection = 0 Make/O/N=(ntraces,4) dfr:wOriginalColors = 0 // Store R,G,B,A for undo WAVE/T wTraceNames = dfr:wTraceNames WAVE wOriginalColors = dfr:wOriginalColors Variable i for (i = 0; i < ntraces; i++) String tname = StringFromList(i, traces) wTraceNames[i][0] = tname // Get current color values String info = TraceInfo(gname, tname, 0) String color = GetTraceColorString(info) Variable red, green, blue, alpha ParseColorString(color, red, green, blue, alpha) // Store for undo wOriginalColors[i][0] = red wOriginalColors[i][1] = green wOriginalColors[i][2] = blue wOriginalColors[i][3] = alpha // Display alpha value in second column wTraceNames[i][1] = num2str(alpha) endfor // Set column titles SetDimLabel 1, 0, 'Trace Name', wTraceNames SetDimLabel 1, 1, 'Alpha', wTraceNames End // ============================================ // Color parsing utilities // ============================================ Function/S GetTraceColorString(String traceInfo) // Extract rgb(x) from trace info Variable rgbStart = strsearch(traceInfo, "rgb(x)=", 0) if (rgbStart < 0) return "(0,0,0,65535)" endif Variable parenStart = strsearch(traceInfo, "(", rgbStart + 7) Variable parenEnd = strsearch(traceInfo, ")", parenStart) if (parenStart < 0 || parenEnd < 0) return "(0,0,0,65535)" endif return traceInfo[parenStart, parenEnd] End Function ParseColorString(String color, Variable &red, Variable &green, Variable &blue, Variable &alpha) // Remove parentheses color = ReplaceString("(", color, "") color = ReplaceString(")", color, "") red = str2num(StringFromList(0, color, ",")) green = str2num(StringFromList(1, color, ",")) blue = str2num(StringFromList(2, color, ",")) // Alpha is optional (4th value) if (ItemsInList(color, ",") >= 4) alpha = str2num(StringFromList(3, color, ",")) else alpha = 65535 // Default to fully opaque endif End // ============================================ // Core function: Set alpha for selected traces // ============================================ Function SetSelectedTracesAlpha(String gname, Variable alpha, Variable updateOriginal) DFREF dfr = root:Packages:TraceOpacityEditor WAVE/T wTraceNames = dfr:wTraceNames WAVE wTraceSelection = dfr:wTraceSelection WAVE wOriginalColors = dfr:wOriginalColors Variable i, ntraces = DimSize(wTraceNames, 0) for (i = 0; i < ntraces; i++) if (wTraceSelection[i] == 0) continue // Skip unselected traces endif String tname = wTraceNames[i][0] if (CmpStr(tname, "(no traces)") == 0) continue endif // Get current RGB from trace (to preserve it) String info = TraceInfo(gname, tname, 0) String color = GetTraceColorString(info) Variable red, green, blue, oldAlpha ParseColorString(color, red, green, blue, oldAlpha) // Apply new alpha while preserving RGB ModifyGraph/W=$gname rgb($tname)=(red, green, blue, alpha) // Update display wTraceNames[i][1] = num2str(alpha) // Update original colors if this is a permanent change if (updateOriginal) wOriginalColors[i][3] = alpha endif endfor End // ============================================ // Control Procedures // ============================================ Function GraphPopupProc(pa) : PopupMenuControl STRUCT WMPopupAction &pa if (pa.eventCode == 2) // Mouse up DFREF dfr = root:Packages:TraceOpacityEditor String/G dfr:gCurrentGraph = pa.popStr BuildTraceListWave(pa.popStr) endif return 0 End Function RefreshListProc(ba) : ButtonControl STRUCT WMButtonAction &ba if (ba.eventCode == 2) // Mouse up DFREF dfr = root:Packages:TraceOpacityEditor SVAR gname = dfr:gCurrentGraph BuildTraceListWave(gname) endif return 0 End Function TraceListBoxProc(lba) : ListBoxControl STRUCT WMListboxAction &lba // Could add double-click handling here if needed return 0 End Function AlphaSliderProc(sa) : SliderControl STRUCT WMSliderAction &sa if (sa.eventCode == 4 || sa.eventCode == 2) // Value changed or mouse up DFREF dfr = root:Packages:TraceOpacityEditor NVAR gCurrentAlpha = dfr:gCurrentAlpha gCurrentAlpha = sa.curval // If live preview is enabled, update graph immediately ControlInfo/W=TraceOpacityPanel livePreviewCheck if (V_Value == 1) SVAR gname = dfr:gCurrentGraph SetSelectedTracesAlpha(gname, gCurrentAlpha, 0) endif endif return 0 End Function LivePreviewCheckProc(cba) : CheckBoxControl STRUCT WMCheckboxAction &cba if (cba.eventCode == 2) // Mouse up if (cba.checked) // When enabling live preview, apply current alpha immediately DFREF dfr = root:Packages:TraceOpacityEditor SVAR gname = dfr:gCurrentGraph NVAR alpha = dfr:gCurrentAlpha SetSelectedTracesAlpha(gname, alpha, 0) endif endif return 0 End Function AlphaSetVarProc(sva) : SetVariableControl STRUCT WMSetVariableAction &sva if (sva.eventCode == 1 || sva.eventCode == 2) // Value changed DFREF dfr = root:Packages:TraceOpacityEditor Slider alphaSlider, win=TraceOpacityPanel, value=sva.dval endif return 0 End Function ApplyAlphaProc(ba) : ButtonControl STRUCT WMButtonAction &ba if (ba.eventCode == 2) // Mouse up DFREF dfr = root:Packages:TraceOpacityEditor SVAR gname = dfr:gCurrentGraph NVAR alpha = dfr:gCurrentAlpha // Store current state for undo before applying StoreCurrentState() SetSelectedTracesAlpha(gname, alpha, 1) endif return 0 End Function StoreCurrentState() DFREF dfr = root:Packages:TraceOpacityEditor SVAR gname = dfr:gCurrentGraph WAVE/T wTraceNames = dfr:wTraceNames WAVE wOriginalColors = dfr:wOriginalColors // Store current state in undo waves Duplicate/O wOriginalColors, dfr:wUndoColors Variable i, ntraces = DimSize(wTraceNames, 0) WAVE wUndoColors = dfr:wUndoColors for (i = 0; i < ntraces; i++) String tname = wTraceNames[i][0] if (CmpStr(tname, "(no traces)") == 0) continue endif String info = TraceInfo(gname, tname, 0) String color = GetTraceColorString(info) Variable red, green, blue, alpha ParseColorString(color, red, green, blue, alpha) wUndoColors[i][0] = red wUndoColors[i][1] = green wUndoColors[i][2] = blue wUndoColors[i][3] = alpha endfor End Function UndoAlphaProc(ba) : ButtonControl STRUCT WMButtonAction &ba if (ba.eventCode == 2) // Mouse up DFREF dfr = root:Packages:TraceOpacityEditor SVAR gname = dfr:gCurrentGraph WAVE/T wTraceNames = dfr:wTraceNames WAVE/Z wUndoColors = dfr:wUndoColors if (!WaveExists(wUndoColors)) DoAlert 0, "Nothing to undo" return 0 endif Variable i, ntraces = DimSize(wTraceNames, 0) for (i = 0; i < ntraces; i++) String tname = wTraceNames[i][0] if (CmpStr(tname, "(no traces)") == 0) continue endif Variable red = wUndoColors[i][0] Variable green = wUndoColors[i][1] Variable blue = wUndoColors[i][2] Variable alpha = wUndoColors[i][3] ModifyGraph/W=$gname rgb($tname)=(red, green, blue, alpha) wTraceNames[i][1] = num2str(alpha) endfor KillWaves/Z dfr:wUndoColors endif return 0 End Function SelectAllProc(ba) : ButtonControl STRUCT WMButtonAction &ba if (ba.eventCode == 2) DFREF dfr = root:Packages:TraceOpacityEditor WAVE wTraceSelection = dfr:wTraceSelection wTraceSelection = 1 endif return 0 End Function SelectNoneProc(ba) : ButtonControl STRUCT WMButtonAction &ba if (ba.eventCode == 2) DFREF dfr = root:Packages:TraceOpacityEditor WAVE wTraceSelection = dfr:wTraceSelection wTraceSelection = 0 endif return 0 End Function InvertSelectionProc(ba) : ButtonControl STRUCT WMButtonAction &ba if (ba.eventCode == 2) DFREF dfr = root:Packages:TraceOpacityEditor WAVE wTraceSelection = dfr:wTraceSelection wTraceSelection = !wTraceSelection endif return 0 End Function ClosePanelProc(ba) : ButtonControl STRUCT WMButtonAction &ba if (ba.eventCode == 2) DoWindow/K TraceOpacityPanel endif return 0 End // ============================================ // TEST DATA CREATION // Execute this function to create test data // ============================================ Function CreateTestDataAndGraph() // Create some sample waves with different data Make/O/N=100 wave1, wave2, wave3, wave4, wave5, wave6 SetScale/I x, 0, 10, wave1, wave2, wave3, wave4, wave5, wave6 // Generate different patterns wave1 = sin(x) wave2 = cos(x) wave3 = sin(x) * 0.5 + 0.5 wave4 = cos(x) * 0.7 wave5 = sin(x * 2) * 0.8 wave6 = cos(x * 0.5) + sin(x * 3) * 0.3 // Add some noise wave1 += gnoise(0.1) wave2 += gnoise(0.1) wave3 += gnoise(0.1) wave4 += gnoise(0.1) wave5 += gnoise(0.1) wave6 += gnoise(0.1) // Create the graph DoWindow/K TestOpacityGraph Display/K=1/W=(100,100,700,500) wave1, wave2, wave3, wave4, wave5, wave6 DoWindow/C TestOpacityGraph DoWindow/T TestOpacityGraph, "Test Graph for Opacity Editor" // Apply different colors to each trace ModifyGraph rgb(wave1)=(65535,0,0) // Red ModifyGraph rgb(wave2)=(0,0,65535) // Blue ModifyGraph rgb(wave3)=(0,52224,0) // Green ModifyGraph rgb(wave4)=(65535,32768,0) // Orange ModifyGraph rgb(wave5)=(32768,0,65535) // Purple ModifyGraph rgb(wave6)=(0,52224,52224) // Cyan // Make lines thicker for visibility ModifyGraph lsize=2 // Add legend Legend/C/N=text0/J/F=0/A=RT // Add axis labels Label left "Amplitude" Label bottom "X Value" Print "Test graph created: TestOpacityGraph" Print "Now run ShowTraceOpacityPanel() or use Graph > Trace Opacity Editor..." End // ============================================ // Alternative: Create overlapping data // for better opacity visualization // ============================================ Function CreateOverlappingTestData() Variable i // Create 10 overlapping sine waves with phase shifts for (i = 0; i < 10; i++) String wname = "overlap" + num2str(i) Make/O/N=200 $wname WAVE w = $wname SetScale/I x, 0, 4*Pi, w // Sine waves with different phases w = sin(x + i * Pi/10) + gnoise(0.05) endfor // Create the graph DoWindow/K OverlapTestGraph Display/K=1/W=(100,100,700,500) DoWindow/C OverlapTestGraph DoWindow/T OverlapTestGraph, "Overlapping Traces - Opacity Demo" // Add all waves with rainbow colors for (i = 0; i < 10; i++) wname = "overlap" + num2str(i) AppendToGraph $wname // Calculate rainbow color (red -> orange -> yellow -> green -> blue -> purple) Variable hue = i / 10 * 360 Variable red, green, blue HSL2RGB(hue, 1, 0.5, red, green, blue) ModifyGraph rgb($wname)=(red*65535, green*65535, blue*65535) endfor ModifyGraph lsize=2 Label left "Amplitude" Label bottom "Phase (radians)" Print "Overlapping test graph created: OverlapTestGraph" Print "Select some traces and change opacity to see effect on overlapping data" Print "Now run ShowTraceOpacityPanel() or use Graph > Trace Opacity Editor..." End // Helper function: Convert HSL to RGB Function HSL2RGB(Variable h, Variable s, Variable l, Variable &r, Variable &g, Variable &b) Variable c = (1 - abs(2*l - 1)) * s Variable x = c * (1 - abs(mod(h/60, 2) - 1)) Variable m = l - c/2 if (h < 60) r = c; g = x; b = 0 elseif (h < 120) r = x; g = c; b = 0 elseif (h < 180) r = 0; g = c; b = x elseif (h < 240) r = 0; g = x; b = c elseif (h < 300) r = x; g = 0; b = c else r = c; g = 0; b = x endif r += m g += m b += m End