[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
multipanel
- Subject: multipanel
- From: Martin Schultz <mgs(at)io.harvard.edu>
- Date: Tue, 23 Mar 1999 16:54:34 -0500
- Newsgroups: comp.lang.idl-pvwave
- Organization: Dept. for Engineering&Applied Sciences,Harvard University
- Xref: news.doit.wisc.edu comp.lang.idl-pvwave:14046
Hi all,
please find below a routine designed to ease one's life with
!p.multi. It was inspired by David F's new tvimage routine which
"honors" the !p.multi keyword. You can use multipanel.pro to:
* get the position vector for the next plot to be made (regardless of
!p.multi)
* set up a multi-panel plot specifying either the number of rows and
cols or
the number of plots per page
* advance to the next (or to any) panel on the page
* turn !p.multi off
There are MARGIN and OMARGIN options which accept 1,2 or 4 values in
normalized coordinates, and (of course) a /NOERASE keyword. If you set
up a page with say 5 panels per page, the screen (or page) will actually
be erased after 5 plots although 6 would fit onto the page and "plain"
!p.multi would do all 6.
Try it out! It's free ;-)
Martin.
--
-------------------------------------------------------------------
Dr. Martin Schultz
Department for Engineering&Applied Sciences, Harvard University
109 Pierce Hall, 29 Oxford St., Cambridge, MA-02138, USA
phone: (617)-496-8318
fax : (617)-495-4551
e-mail: mgs@io.harvard.edu
Internet-homepage: http://www-as.harvard.edu/people/staff/mgs/
-------------------------------------------------------------------
;-------------------------------------------------------------
; $Id$
;+
; NAME:
; MULTIPANEL
;
; PURPOSE:
; This routine provides an easy-to-use interface to
; IDL's !p.multi mechanism and it can be used
; to retrieve the position vector of the next plot
; in line (even if !p.multi is not set). You can
; specify plot margins and a page margin in normalized
; coordinates.
; For multi-panel plots, multipanel works in three
; stages (somewhat similar to XInterAnimate):
; 1) set up a multi-panel page by specifying the number
; of rows and columns or the maximum number of plots
; on the page.
; 2) advance to the next plot position and return that
; position. If the page was filled (i.e. the maximum
; number of plots (advance steps) has been made, it
; will be erased (unless /NOERASE is set).
; 3) turn the multi-panel environment off and reset margin
; values.
; Subsequent PLOT, MAP_SET, etc. commands should use the
; POSITION vector returned from this routine and they should
; use the /NOERASE keyword.
;
; CATEGORY:
; Plotting tools
;
; CALLING SEQUENCE:
; Get position of next plot
; MULTIPANEL,position=p
;
; Setting up multi-panel pots
; MULTIPANEL,[rows=r,cols=c | nplots=n] [,options]
;
; Advance to the next panel (and get its position)
; MULTIPANEL,/advance [,position=p] [,options]
;
; Reset multi-panel environment to start over at the first panel
; MULTIPANEL,/reset [,options]
;
; Turn multi-panel environment off (reset !p.multi and some other
; parameters)
; MULTIPANEL,/OFF
;
; INPUTS:
; Optionally you can give the number of plots (keyword NPLOTS) as
; a parameter instead. Th euse of a parameter will override the
; keyword.
;
; KEYWORD PARAMETERS:
; General keywords (honored under all circumstances)
; -------------------------------------------------------------------
; POSITION -> Returns a 4-element vector with the position of
; the next plot in the line. As always this vector contains
; [X0,Y0,X1,Y1].
;
; MARGIN -> specify a margin around the plot in normalized
; coordinates. This keyword does not change any IDL
; system variables and will thus only become "visible"
; if you use the POSITION returned by MULTIPANEL in subsequent plot
; commands.
; MARGIN can either be one value which will be applied to
; all four margins, or a 2-element vector which results in
; equal values for the left and right and equal values for
; the bottom and top margins, or a 4-element vector with
; [left,bottom,right,top].
;
; OMARGIN -> specify a page margin around all panels in normalized
; coordinates. Works like MARGIN.
;
; Note that you can change the values of MARGIN and OMARGIN after
; you set up your plot page. The values given with MARGIN and OMARGIN
; remain active until you specify a new one or use the /OFF keyword.
;
; /NOERASE -> This keyword prevents erasing the screen (or page)
; when setting a multi-panel environment or after a page was
; filled. NOERASE is automatically turned ON when the /OFF
; keyword is given.
;
;
; Informational keywords:
; -------------------------------------------------------------------
;
; FIRSTPANEL -> returns 1 if the current plotting panel is
; the first one on a page
;
; LASTPANEL -> returns 1 if the current plotting panel is the last
; on a page
;
;
; Setting up a multi-panel page:
; -------------------------------------------------------------------
; ROWS, COLS -> specify the number of rows and columns
; you want on your page. If one is specified, the
; other one must be given as well. Alternatively,
; you can use the NPLOTS keyword.
;
; NPLOTS -> maximum number of plots on one page. The
; number of rows and columns is automatically computed
; so that the plots "approach a square" and they are
; returned in the ROWS and COLS keywords. Setting
; NPLOTS to a ROWS*COLS value is equivalent to using
; ROWS and COLS. If you specify an "uneven" number
; (e.g. 5), multipanel,/advance will erase the page
; after 5 plots instead of 6.
;
; /PORTRAIT -> Normally, ROWS tends to be larger than COLS
; when NPLOTS is used (e.g. 12 gives 4 rows and 3 cols).
; Use this keyword to revert this behaviour.
;
; /LANDSCAPE -> Make ROWS larger than COLS if necessary. This
; is the default. Thi skeyword is actually unnecessary and
; was introduced for purely aesthetic reasons (symmetry).
;
;
; Advance to the next plot:
; -------------------------------------------------------------------
; /ADVANCE -> this keyword issues a hidden plot command to find
; out the position of the next plot to be made. The position
; is then returned in the POSITION keyword. The value of
; !P.MULTI[0] is increased afterwards so that you can issue
; your plot command without explicitely specifying the
; position or /NOERASE. When the maximum number of plots set
; with NPLOTS is reached, MULTIPANEL,/ADVANCE will erase the
; screen and reset the plot position to the upper left corner.
;
; CURPLOT -> use this keyword to advance to a specific plot position
; on the page. If you specify 0, the screen will be erased.
; CURPLOT also returns the current plot number. If you don't
; want to set the plot number but just query it, you must pass
; an undefined variable (see procedure UNDEFINE.PRO).
;
;
; Reset the plot position (and erase the screen)
; -------------------------------------------------------------------
; /RESET -> does just this.
;
;
; Turn multi-panel environment off
; -------------------------------------------------------------------
; /OFF -> Overrides all other keywords. Resets !p.multi to 0.
;
;
; OUTPUTS:
; none.
;
; SUBROUTINES:
; function GET__PLOTPOSITION() issues a dummy plot command
; and returns the plot position from the ![xy].window
; variables.
;
; REQUIREMENTS:
; none. (example uses RECTANGLE.PRO)
;
; NOTES:
; Side effect: Opens a window in standard size if none was open
; before. This happens because the next plot position is
; determined by issuing a hidden (or dummy) plot command with
; no visible output.
;
; Make sure to use POSITION=P,/NOERASE with all your PLOT, MAP_SET
; CONTOUR, etc. commands. In fact, you don't _need_ /NOERASE for
; PLOT, but it guarantees consistent behaviour with MAP_SET etc.)
; A PLOT command without /NOERASE will advance to the next panel
; and thereby interfere with MULTIPANEL's plot counter.
;
; If you don't use MARGIN and OMARGIN, the values in ![XY].margin
; ![XY].omargin will take effect.
;
; A common block is used to store the current plot number and
; margin information. This limits the use to one window at a time
; (although you can save all parameters elsewhere and supply them
; to the routine on a window-by-window basis).
;
; EXAMPLES:
; ; just retrieve the position of the next plot
; ; ---------------------------------------------------------------
; MULTIPANEL,position=p
; PLOT,findgen(10),color=1,position=p,/noerase
;
; ; same thing but use a specific margin (2-element form)
; ; ---------------------------------------------------------------
; MULTIPANEL,margin=[0.1,0.05],position=p
; PLOT,findgen(10),color=1,position=p,/noerase
;
; ; (if you want to see both previous plots together use
; ' /NOERASE with MULTIPANEL)
;
; ; multi-panel use:
; ; ---------------------------------------------------------------
; MULTIPANEL,NPlots=5 ; set up for 5 plots
; for i=0,4 do begin
; MULTIPANEL,Position=p,margin=0.04 ; get current plot position
; plot,findgen(10),color=1,position=p,/noerase ; plot
; MULTIPANEL,/Advance,/NoErase ; go to next panel
; ; note that the screen would be erased after the last plot
; ; without the /NoErase keyword.
; endfor
; ; now let's draw a frame around everything
; MULTIPANEL,/OFF,omargin=0.01,margin=0.,position=p,/noerase
; Rectangle,p,xvec,yvec ; <<< uses RECTANGLE.PRO !!
; plots,xvec,yvec,color=1,/norm
;
; MODIFICATION HISTORY:
; mgs, 19 Mar 1999: VERSION 1.00
; mgs, 22 Mar 1999: - improved documentation, changed OMARGIN
; to accept normalized coordinates.
; - position now also returned if turned OFF
; - added FIRSTPANEL and LASTPANEL keywords
; - allow NPLOTS to be specified as parameter
;
;-
; Copyright (C) 1999, Martin Schultz, Harvard University
; This software is provided as is without any warranty
; whatsoever. It may be freely used, copied or distributed
; for non-commercial purposes. This copyright notice must be
; kept with any copy of this software. If this software shall
; be used commercially or sold as part of a larger package,
; please contact the author to arrange payment.
; Bugs and comments should be directed to mgs@io.harvard.edu
; with subject "IDL routine multipanel"
;-------------------------------------------------------------
function get__plotposition,noerase=noerase,advance=advance
COMMON PLOT__MARGINS, activemargin, margingiven, pg_, $
maxplots, curplot
; increase current plot number except if noadvance was given
if (keyword_set(advance)) then curplot = ( curplot + 1 ) > 0
if (curplot ge maxplots) then curplot = 0
; set new value of !p.multi[0] according to curplot
; this has to be done in order to achieve the same behaviour
; with map_set as with plot
; note that !p.multi will not be zero at any time, thus the
; next plot will not erase the screen automatically.
maxrc = ( !p.multi[1]*!p.multi[2] ) > 1
!p.multi[0] = maxrc - curplot
; save this value of !p.multi[0]
save_p0 = !p.multi[0]
; if (not keyword_set(advance)) then !p.multi[0] = !p.multi[0] + 1
; if (!p.multi[0] gt maxplots) then !p.multi[0] = maxrc
; print,'get__.... : curplot,!p.multi[0] = ',curplot,!p.multi[0]
; erase screen or page manually if necessary
if (not keyword_set(noerase) AND curplot eq 0) then erase
; create an empty dummy plot and return position
plot,findgen(10),/nodata,xstyle=4,ystyle=4
; reset !p.multi to value it had before dummy plot
!p.multi[0] = save_p0
; get plot window position :
; 1. extract it from the window coordinates
result = [ !x.window[0], !y.window[0], $
!x.window[1], !y.window[1] ]
; apply margin correction
result = result+activemargin
; shrink to fit into page size
result[[0,2]] = result[[0,2]]*pg_.wx + pg_.ox
result[[1,3]] = result[[1,3]]*pg_.wy + pg_.oy
return, result
end
; $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
pro multipanel,ParNPlots, $
rows=rows, cols=cols, margin=margin, omargin=omargin, $
nplots=nplots, portrait=portrait, landscape=landscape, $
position=position, $
advance=advance, $
reset=reset, noerase=noerase, $
curplot=tcurplot,firstpanel=firstpanel,lastpanel=lastpanel, $
off=off
COMMON PLOT__MARGINS, activemargin, margingiven, pg_, $
maxplots, curplot
if (n_elements(activemargin) eq 0) then begin
pg_ = { wx:1., wy:1., ox:0., oy:0. } ; page width and offset
activemargin = [ 0., 0., 0., 0. ]
activeomargin = [ 0., 0., 0., 0. ]
margingiven = 0 ; no user-supplied margin
; extra variable , because user may supply
; [0,0,0,0] as margin. For omargin, 0. is the
; default.
maxplots = 1
curplot = 0
endif
; ----------------------------------------------------------------------
; highest significance is given to the keyword that turns !p.multi off
; ----------------------------------------------------------------------
if (keyword_set(OFF)) then begin
!p.multi = 0
; reset margin values
pg_ = { wx:1., wy:1., ox:0., oy:0. } ; page width and offset
activemargin = [ 0., 0., 0., 0. ]
margingiven = 0
maxplots = 1
curplot = 0
Advance = 0 ; we don't want to advance any further
NoErase = 1 ; do not erase previous work
endif
; ----------------------------------------------------------------------
; now let's deal with keywords that turn !p.multi ON
; we need either rows and cols or nplots
; nplots is given priority since it can then return the computed
; number of rows and columns in the respective variables
; ----------------------------------------------------------------------
; set default for position
position = [ 0., 0., 1., 1. ]
IsTurnedOn = 0
if (n_elements(Advance) eq 0) then $ ; trick to find out whether turned off
Advance = keyword_set(Advance)
if (n_params() gt 0) then nplots = fix(ParNPlots[0])
if (n_elements(nplots) gt 0) then begin
if (nplots le 0) then begin
message,'NPLOTS must be positive!',/Continue
return
endif
cols = ( fix( sqrt(nplots[0]-1) ) + 1 ) > 1
rows = ( fix( (nplots[0]-1)/cols ) + 1 ) > 1
; switch rows and cols if portrait is selected
; (landscape is ignored and just included for
; cosmetic reasons)
if (keyword_set(portrait)) then begin
tmp = rows
rows = cols
cols = tmp
endif
maxplots = nplots > 1
IsTurnedOn = 1
endif else $
if (n_elements(rows) gt 0 AND n_elements(cols) gt 0) then begin
maxplots = rows*cols > 1
IsTurnedOn = 1
endif
if (IsTurnedOn) then begin
!p.multi = [ 0, cols, rows ]
; reset margin values (may be overwritten by explicit margin values)
pg_ = { wx:1., wy:1., ox:0., oy:0. } ; page width and offset
activemargin = [ 0., 0., 0., 0. ]
margingiven = 0
curplot = 0
; prevent advancing to second frame
Advance = 0
endif
; ----------------------------------------------------------------------
; does the user want to reset !p.multi to the first plot?
; ----------------------------------------------------------------------
IsReset = 0
if (IsTurnedOn eq 0 AND keyword_set(Reset)) then begin
curplot = 0
IsReset = 1
; prevent advancing to second frame
Advance = 0
endif
; ----------------------------------------------------------------------
; get current number of rows and columns from !p.multi
; This is used to limit the margin range and to determine whether
; the maximum number of plots has been reached
; ----------------------------------------------------------------------
nrows = !p.multi[1] > 1
ncols = !p.multi[2] > 1
; ----------------------------------------------------------------------
; Handle the margin keywords. If it is a single value, use it for
; all four margins. Two values indicate equal x and equal y margins.
; Four values can be stored directly.
; Invert right and top margin values in activemargin so we just need
; to add it onto position.
; Make sure that left is left, and right is right, etc.
; ----------------------------------------------------------------------
if (n_elements(omargin) gt 0) then begin
tmargin = omargin
if (n_elements(omargin) eq 1) then tmargin = replicate(omargin,4)
if (n_elements(omargin) eq 2) then tmargin = [ omargin, omargin ]
if (n_elements(tmargin) ne 4) then begin
message,'OMARGIN must have 1, 2, or 4 elements!',/Continue
return
endif
tmargin = abs(tmargin)
; Limit OMARGIN to a total of 0.9 at most
; issue a warning if they exceed a total of 0.5
if (tmargin[0]+tmargin[2] gt 0.5) then $
message,'LEFT/RIGHT omargins exceed 0.5!',/INFO
if (tmargin[0]+tmargin[2] gt 0.9) then $
tmargin[[0,2]] = 0.25
if (tmargin[1]+tmargin[3] gt 0.5) then $
message,'LEFT/RIGHT omargins exceed 0.5!',/INFO
if (tmargin[1]+tmargin[3] gt 0.9) then $
tmargin[[1,3]] = 0.25
; Translate tmargin into "page" parameters of common block
pg_.wx = 1. - (tmargin[0]+tmargin[2])
pg_.wy = 1. - (tmargin[1]+tmargin[3])
pg_.ox = tmargin[0]
pg_.oy = tmargin[1]
endif
if (n_elements(margin) gt 0) then begin
tmargin = margin
if (n_elements(margin) eq 1) then tmargin = replicate(margin,4)
if (n_elements(margin) eq 2) then tmargin = [ margin, margin ]
if (n_elements(tmargin) ne 4) then begin
message,'MARGIN must have 1, 2, or 4 elements!',/Continue
return
endif
tmargin = abs(tmargin)
; make sure plot has still wiggle room
; (note that margin is applied to each plot!)
if (nrows*(tmargin[0]+tmargin[2]) ge 1.) then begin
tmargin[[0,2]] = 0.5/nrows - 0.01
message,'LEFT/RIGHT margins too large! Set to maximum of' $
+ string(tmargin[0],format='(f8.3)'),/Continue
endif
if (ncols*(tmargin[1]+tmargin[3]) ge 1.) then begin
tmargin[[1,3]] = 0.5/ncols - 0.01
message,'BOTTOM/TOP margins too large! Set to maximum of' $
+ string(tmargin[1],format='(f8.3)'),/Continue
endif
; invert right and top values
tmargin[[2,3]] = -tmargin[[2,3]]
; store in common block for later use
activemargin = tmargin
margingiven = 1
endif
; ----------------------------------------------------------------------
; Set plot position if specifically requested
; Range checking done automatically in get__plotposition
; ----------------------------------------------------------------------
if (n_elements(tcurplot) gt 0) then $
curplot = fix(tcurplot)
; ----------------------------------------------------------------------
; Return position of next plot
; This automatically advances the plot position unless the
; NoAdvance keyword is set!!
; ----------------------------------------------------------------------
; set margin values in !x and !y to zero only if a user
; margin was given
if (margingiven) then begin
!x.margin = [0,0]
!y.margin = [0,0]
endif
; determine position of next plot
position = get__plotposition(noerase=noerase,Advance=Advance)
; return current plot number
tcurplot = curplot
firstpanel = (curplot eq 0)
lastpanel = (curplot eq (maxplots-1) )
return
end