[inf] to reference last trace added to graph

I often find myself trying to modify the appearance of a trace just after adding it to a graph. For this, I'd like an easy way to reference the last trace that was added to a graph.

Unless I'm missing something, the following doesn't work as expected:

Modifygraph lsize[inf] = 5

 

Right now, I'm using something like

Static Function/S LastAddedTraceName([win])
    String win
    win = selectstring(paramisdefault(win),win,"")
    return stringfromlist(itemsinlist(TraceNameList(win, ";", 1))-1, TraceNameList(win, ";", 1))
End

to call

Modifygraph lsize($lastaddedtracename())=5

but this is slow because of the string parsing.

 

Is there a better way? I understand that I could just extract the number of traces in the graph to save a bit of the string parsing, and use the [] syntax instead of the () syntax, but this is still tedious.

For the wishlist, I'd like to suggest: Have [inf] reference the last added trace.

A better way might be to simply right-click the trace and choose a new color from the popup menu.

This should bypass the need for a loop.

Function append_mytracestyle(ww,ls,...)
     wave ww
     variable ls, ...
     string ...

     string tname

     appendtograph ww
     tname = StringFromList(inf,TraceNameList(...))
     modifygraph lsize($tname)=ls

     return 0
end

We might also ask for the ability to change a trace name.

Function append_mytracestyle(ww,ls,...)
     wave ww
     variable ls, ...
     string ...

     string tname, tmpname = "recent"

     tname = ... // set a name for the trace

     appendtograph/TN=$tmpname ww
     modifygraph lsize($tmpname)=ls

     RenameTrace $tmpname, $tname  // <-- DOES NOT EXIST (but may provide other useful options)

     return 0
end    

 

Thanks for the suggestions, guys.

Unfortunately, right-clicking wouldn't work for me as I'm doing this in a script that sometimes adds >100 traces.

 

I was looking at JJ's suggestion and noticed that

print stringfromlist(inf,"one;two;three;")

didn't give me the last element of the list as I was hoping for. This means it would need the same clumsy

stringfromlist(itemsinlist()-1,…)

syntax that I was using.

 

I also realized that some of my issues came from the rather slow performance of stringfromlist and/or itemsinlist, which I had only tested on Igor 7. This improved quite a bit on Igor 8.

 

 

However, I still would argue that something

Modifygraph lsize[inf] = something

would be a lot easier and cleaner.

 

 

This may not be the answer to your specific problem, but for anyone wondering why every new trace is plotted with the same appearance as the previous one, there are two useful ways to change this:

To set graph preferences so that newly added traces follow a pattern of styles:

displayHelpTopic "Graph Preferences"

To adjust trace styles after plotting many traces:

Graph > Packages > Make Traces Different

It seems we might be in need of a few new features ...

* Problem: We have to invoke multiple steps to track the names of the traces that were just added to a graph as well as the last added trace name
Answer: The operations appendtograph and display should generate globals S_tracenames and S_lasttracename to document the list of traces generated and the last one added.

* Problem: We have to get invoke nested calls to multiple functions to get the last item in list functions.
Answer: StringFromList and other list functions accept "magic" token of lastitem in the index count (as well as lastitem - N to count backward when needed)
Additional Answer: ReverseList(...) -- function to reverse the order of a list (since index = 0 works but index = inf does not)

* Problem: We can name traces at the moment when they are added but we cannot rename them without removing them and re-adding them.
Answer: RenameTrace ... -- operation to rename a trace on a graph

You may find this of use:

Function LastTraceIndex(graphName)
    String graphName
   
    if (strlen(graphName) == 0)
        graphName = WinName(0,1)    // Top graph
    endif
    if (strlen(graphName) == 0)
        return -1       // No such graph
    endif
   
    String list = TraceNameList(graphName, ";", -1)
    Variable numItems = ItemsInList(list)
    return numItems -1  // -1 if there are no traces in the graph
End

Function Demo()
    Make/O jack=sin(x/8), joe=cos(x/8)
    Display jack, joe
    ModifyGraph lsize[LastTraceIndex("")] = 5
End

 

In reply to by hrodstein

I'll have to take back my statement that my code runs faster on Igor 8. I think the problem is mostly that TraceNameList is slow, especially if many traces are involved (as there are in my case), see the following example that uses hrodstein's code:

 

#include <FunctionProfiling>

Function Demo()
    Variable MaxWaves = 1000
   
    ColorTab2Wave Rainbow
    Wave M_colors
    Setscale/I x, 0, MaxWaves-1, M_colors
   
    Display/N=Win
    Variable i
    for(i=0;i<MaxWaves;i+=1)
        Make/O/N=(100) $("Wave"+num2istr(i))/Wave=w
        w=i
        AppendToGraph/W=Win w
        Modifygraph rgb[LastTraceIndex("Win")] = (M_colors(i)[0], M_colors(i)[1], M_colors(i)[2])
    endfor
End

Function LastTraceIndex(graphName)
    String graphName
   
    if (strlen(graphName) == 0)
        graphName = WinName(0,1)    // Top graph
    endif
    if (strlen(graphName) == 0)
        return -1       // No such graph
    endif
   
    String list = TraceNameList(graphName, ";", -1)
    Variable numItems = ItemsInList(list)
    return numItems -1  // -1 if there are no traces in the graph
End

 

FunctionProfiling of Demo() gives:

Total time: 3.44096, Time in Function code: 3.44096 (100%)
Top function percentages:
Function Procedure LastTraceIndex: 88%

Annotated Top Functions:

*******************************************************************************************
Function: Procedure LastTraceIndex; Percent total 88%
*******************************************************************************************
[00]*             |Function LastTraceIndex(graphName)
[00]              |    String graphName
[00]              |    
[00]              |    if (strlen(graphName) == 0)
[00]              |        graphName = WinName(0,1)    // Top graph
[00]              |    endif
[00]              |    if (strlen(graphName) == 0)
[00]              |        return -1       // No such graph
[00]              |    endif
[00]              |    
[88]*********     |    String list = TraceNameList(graphName, ";", -1)
[00]*             |    Variable numItems = ItemsInList(list)
[00]              |    return numItems -1  // -1 if there are no traces in the graph
[00]              |End

 

Unfortunately, that long time is not surprising.

Finding a trace name is an n-squared problem due to need to determine the instance number. Any built-in routine to get the name of the last trace will also take pretty much the same time. This includes the idea of saving the name as traces are added.

So, the original idea of some syntax that specifies the last trace without needing a name, is a good idea. If traces could be accessed by numeric index, we could use -1 to access the last and -2 to access the next to last etc.

 

 

The following two changes execute in about 0.1 s on my machine:

Function Demo1()
    Variable MaxWaves = 1000
    KillWindow/Z Win
   
    ColorTab2Wave Rainbow
    Wave M_colors
    Setscale/I x, 0, MaxWaves-1, M_colors
   
    Display/N=Win
    Variable i
    String thisName
    for(i=0;i<MaxWaves;i+=1)
        thisName = "Wave"+num2istr(i)
        Make/O/N=(100) $(thisName)/Wave=w
        w=i
        AppendToGraph/W=Win w
        Modifygraph rgb($thisName) = (M_colors(i)[0], M_colors(i)[1], M_colors(i)[2])
    endfor
End

Function Demo2()
    Variable MaxWaves = 1000
    KillWindow/Z Win
   
    ColorTab2Wave Rainbow
    Wave M_colors
    Setscale/I x, 0, MaxWaves-1, M_colors
   
    Display/N=Win
    Variable i
    String thisName, thisTraceName
    for(i=0;i<MaxWaves;i+=1)
        thisName = "Wave"+num2istr(i)
        thisTraceName = "trace_" + thisName
        Make/O/N=(100) $(thisName)/Wave=w
        w=i
        AppendToGraph/W=Win w /TN=$(thisTraceName)
        Modifygraph rgb($thisTraceName) = (M_colors(i)[0], M_colors(i)[1], M_colors(i)[2])
    endfor
End

Both of these rely on the fact that at least in your example you know what the name of the trace will be when you append it to the graph (either because you specify it with /TN in Demo2 or you know the name of the wave and that there are no other instances of a trace with the same name in Demo1). Since you know the trace's name, you can pass that directly with ModifyGraph.

Another possibility would be to call TraceNameList before your loop to determine the number of traces already on the graph. Then after you append each trace, increment the value by 1. Then you can always use the correct trace number in the ModifyGraph command but you only need to call TraceNameList once.

 

In reply to by Larry Hutchinson

Quote:
If traces could be accessed by numeric index, we could use -1 to access the last and -2 to access the next to last etc.

Traces can be accessed by numeric index.

DisplayHelpTopic "ModifyGraph for Traces"  // See "This syntax is used for style macros"

 

Thanks guys. The /TN workaround did the job for me.

It would still be very convenient if the suggestion of being able to address indices "from the end" would be implemented in a future version of Igor.