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 10

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More