Print names of traces in graph marquee

The code below installs an item Print Traces In Marquee in the graph marquee menu that lets you print the names of the traces in the marquee.
To test, copy the code below in your procedure window and then execute this on the command line:
make/o w0=100-x, w1=x^2/128, w2=2*x+5, w3=3*x;Display w0, w1, w2, w3;Append w0,w2 vs w1;legend

Then, colorize the traces (menu Graph|Packages|Make Traces Different, press "Commonly-Used Colors") and drag a marquee on the graph, use the context-menu (right-click (windows) or ctrl-click (macintosh)).

Menu "GraphMarquee"
    "Print Traces In Marquee", /Q, print GetTracesInMarquee()
End

function/S GetTracesInMarquee()
    string tracesInMarquee = "" // result
// get marquee coordinates
    // one may want to check whether these axes really exist since Igor throws an error otherwise
    GetMarquee/K/Z left, bottom
    if( V_Flag == 0 )
        return ""   // there was no marquee
    endif
//  print V_left, V_right, V_top, V_bottom
// convert marquee coordinates to polygon
    Make/FREE/N=5 xPoly={V_Left, V_left, V_right, V_right, V_left}
    Make/FREE/N=5 yPoly={V_Top, V_bottom, V_bottom, V_top, V_top}
// get info about traces
    string traces = TraceNameList("", ";", 5)   // omit hidden traces
    string trace, traceX, info
    variable n, lastInstanceChar, instance
// step through traces
    for( n = 0; n < ItemsInList(traces); n += 1 )
        // get y wave
        trace = StringFromList(n, traces)
        wave yWave = TraceNameToWaveRef("", trace)
        // that was easy, now get x wave
        if( WaveExists(XWaveRefFromTrace("", trace)) )      // has x wave?
            wave xWave = XWaveRefFromTrace("", trace)
            traceX = NameOfWave(xWave)
        else
            Make/FREE/N=(numpnts(yWave)) tmpXWave   // make x wave, this could be costly
            wave xWave = tmpXWave
            xWave = leftx(yWave) + p*deltax(yWave)
            traceX = ""
        endif
        // find out if its in the marquee
        FindPointsInPoly xWave, yWave, xPoly, yPoly // probably costly
        wave W_inPoly
        W_inPoly = 1/W_inPoly   // convert zeros to infs
        WaveStats/Q W_inPoly
        // if it's in there, V_npnts should be non-zero
        if( V_npnts > 0 )   // only non-inf values are counted
            if( strlen(traceX) > 0 )
                trace += " vs "+traceX
            endif
            tracesInMarquee = AddListItem(trace, tracesInMarquee)
        endif
    endfor
    return tracesInMarquee
end

Hello Wolfgang,

I like your approach using FindLevel (that you suggested on the mailing list) better, because it can handle the case where no points lay inside the marquee, but the trace does (at least if visualized using 'lines between points'). See the attached image.
Additionally your code does not take into account that a trace can be displayed versus other axes than left and bottom.
And lastly the trace can have an offset, which makes it move out of or into the marquee.

I propose the following:
function/s findTracesInMarquee()
    string tracestr=tracenamelist("",";",5),tstr,infostr,tracesInMarquee=""
    variable i,a,b,offsetx,offsety
    for(i=0;i<itemsinlist(tracestr);i+=1)
        tstr=stringfromlist(i,tracestr)
        infostr=traceinfo("",tstr,0)
        sscanf stringbykey("offset(x)",infostr,"="),"{%f,%f}",offsetx,offsety
        getmarquee $stringbykey("XAXIS",infostr),$stringbykey("YAXIS",infostr)
        if(strlen(stringbykey("XWAVE",infostr))==0)     // trace is display vs internal scaling
            wave yWave=tracenametowaveref("",tstr)
            switch(0)
                case 0:
                    findlevel/q/r=(V_left-offsetx,V_right-offsetx) yWave, V_bottom-offsety
                    if(V_flag==0)
                        tracesInMarquee=addlistitem(tstr,tracesInMarquee)
                        break
                    endif
                case 1:
                    findlevel/q/r=(V_left-offsetx,V_right-offsetx) yWave, V_top-offsety
                    if(V_flag==0)
                        tracesInMarquee=addlistitem(tstr,tracesInMarquee)
                        break
                    endif
                case 2:
                    wavestats/q/r=(V_left-offsetx,V_right-offsetx) yWave
                    if(V_avg>=V_bottom-offsety && V_avg<=V_top-offsety)
                        tracesInMarquee=addlistitem(tstr,tracesInMarquee)
                        break
                    endif
            endswitch
        else            // trace is displayed vs xwave
            wave yWave=tracenametowaveref("",tstr)
            wave xwave=$stringbykey("XWAVE",infostr)
            findlevel/q/p xwave,V_left
            if(V_flag!=0)
                break
            endif
            a=V_levelX
            findlevel/q/p xwave,V_right
            if(V_flag!=0)
                break
            endif
            b=V_levelX
            switch(0)
                case 0:
                    findlevel/q/r=[a,b] yWave, V_bottom-offsety
                    if(V_flag==0)
                        tracesInMarquee=addlistitem(tstr,tracesInMarquee)
                        break
                    endif
                case 1:
                    findlevel/q/r=[a,b] yWave, V_top-offsety
                    if(V_flag==0)
                        tracesInMarquee=addlistitem(tstr,tracesInMarquee)
                        break
                    endif
                case 2:
                    wavestats/q/r=[a,b] yWave
                    if(V_avg>=V_bottom-offsety && V_avg<=V_top-offsety)
                        tracesInMarquee=addlistitem(tstr,tracesInMarquee)
                        break
                    endif
            endswitch
        endif
    endfor
    return tracesInMarquee
end


I don't know what happens if the xWave is not monotonic. Also a multiplier offset is not taken into consideration.

Andreas
marquee.png (18.41 KB)
Hi Andreas,

thanks for your snippet, it's always great to get new ideas. In all honesty, the credits for the FindLevel method you so nicely implemented should go to Holger Taschenberger who proposed it on the list (wasn't me!).

Although I did not try to implement a complete algorithm in the first place, your treatment of the axes (I noted that deficiency in my code) and inclusion of possible offsets (I did not even think of those) are marked improvements. I also liked your clever treatment of the case when there is no xWave. It took me some time to understand your cases-with-fall-through trick (you need that for the break to work, right?) -- devious!

As you say, the mulOffset could be included, probably by replacing V_...-offset... with (V_...-offset...)/mulOffset..., where the mulOffset can be read from your infostr in a similar way as the offsets.

I tried to play around with your code to make it "watertight", but encountered some additional difficulties due to the fact that Igor graphs can be complicated beasts:
(1) The graph marquee coordinates can be reversed if the axis limits are inverted (i.e., V_left > V_right, etc.).
(2) If traces have been appended using the /VERT flag, or if the axes have been swapped globally (ModifyGraph swapXY=1 is in effect), the graph marquee coordinates can have unexpected values (e.g., V_left is a y value instead of an x value).
(3) Things get nasty for unequally spaced xWaves (as you already surmised), they get really nasty if the xWave is unsorted. The difficulties hidden in both cases are easily checked if you draw a wave with the DrawWave tool. I think that a general algorithm should cover these cases as well.

Issues (1) and (2) hold, of course, for any algorithm but they might more benign in Rick Gerkin's original TraceFromPixel-grid algorithm (see mailing list) or with mine. Issue (3) seems difficult, if not untractable, with the FindLevel method. I'd be interested if you found a way through that quagmire.

Finally, your worry about the case where a wave is "only passing through" -- would you expect that case to be treated differently depending on the drawing mode?
If the mode is 3 (markers only), I would be taken aback as a user if a function includes traces with points clearly outside the marquee!

Wolfgang
harneit wrote:
In all honesty, the credits for the FindLevel method you so nicely implemented should go to Holger Taschenberger who proposed it on the list (wasn't me!).


Oh I see, then thanks to Holger. I hope he follows igorexchange as well as the mailing list...

harneit wrote:
As you say, the mulOffset could be included, probably by replacing V_...-offset... with (V_...-offset...)/mulOffset..., where the mulOffset can be read from your infostr in a similar way as the offsets.


Right.

harneit wrote:
I tried to play around with your code to make it "watertight", but encountered some additional difficulties due to the fact that Igor graphs can be complicated beasts:
(1) The graph marquee coordinates can be reversed if the axis limits are inverted (i.e., V_left > V_right, etc.).
(2) If traces have been appended using the /VERT flag, or if the axes have been swapped globally (ModifyGraph swapXY=1 is in effect), the graph marquee coordinates can have unexpected values (e.g., V_left is a y value instead of an x value).
(3) Things get nasty for unequally spaced xWaves (as you already surmised), they get really nasty if the xWave is unsorted. The difficulties hidden in both cases are easily checked if you draw a wave with the DrawWave tool. I think that a general algorithm should cover these cases as well.

Issues (1) and (2) hold, of course, for any algorithm but they might more benign in Rick Gerkin's original TraceFromPixel-grid algorithm (see mailing list) or with mine. Issue (3) seems difficult, if not untractable, with the FindLevel method. I'd be interested if you found a way through that quagmire.


Mmh, I must admit I haven't foreseen all these pitfalls indeed. I think in that case, the TraceFromPixel approach is the most robust one, simply because it iterates through all the pixels of the marquee.

harneit wrote:
If the mode is 3 (markers only), I would be taken aback as a user if a function includes traces with points clearly outside the marquee!


I totally agree.

Unfortunately I have not enough time to play around with this.
But in the end you could use one of the three algorithms even if it doesn't consider all the strangest cases as long as you're aware of the constraints.

Forum

Support

Gallery

Igor Pro 9

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More