; docformat = 'rst'
;+
; :Author: Paulo Penteado (pp.penteado@gmail.com), May/2010
; :History: Improved on algorithm to decide on tick locations (created
; pp_plot_decideintervals). Jul/2011
; :Version: 20110722
;-
pro pp_plot_decideintervals,dprange,dticks,dint,decide=decide,newrange=newrange,dminor=dminor,newdticks=newdticks;New algorithm
compile_opt idl2,logical_predicate,hidden
intervals=[2d0,1d0,1d0,0.5d0];,0.25d0] ;Possible interval multipliers to use
intrats=[0.2d0,0.1d0,1d0,0.5d0];,0.25d0] ;Possible normalized interval multipliers
minors=[4,10,10,5]
nintervals=n_elements(intervals)
ints=[6,5,4,3] ;Possible number of intervals
nints=n_elements(ints)
mindiff=!values.d_infinity
for i=0,nints-1 do begin ;Try every number of intervals, to find out which one gives nicer intervals
dints=double(dprange[1]-dprange[0])/ints[i]
al=alog10(dints)
dints=al ge 0d0 ? 1d1^((al mod 1)-1d0) : 1d1^(al mod 1)
tmp=min(abs(dints-intrats),minloc)
if ((tmp-mindiff) lt -(machar(/double)).eps) then begin
mindiff=tmp
minint=intervals[minloc]
sints=ints[i]
dint=intrats[minloc]*1d1^ceil(al)
dminor=minors[minloc]
endif
;print,ints[i],minint,tmp,sints,dint
endfor
newrange=dint*[floor(dprange[0]/dint),ceil(dprange[1]/dint)]
dticks=sints+1d0
newdticks=(newrange[1]-newrange[0])/dint
end
;+
; :Hidden: Intended to be called only by `pp_plot`
; :Description: Calculates the tick values, numbers, intervals and data ranges for `pp_plot` below.
;-
pro pp_plot_maketicks,dprange,drange,d,dtickinterval,dticks,dint,dstyle,dtickv,det,yaxis=yaxis,decide=decide,dminor=dminor
compile_opt idl2,hidden
;Defaults
yaxis=n_elements(yaxis) eq 1 ? yaxis : 0B
print,decide ? "pp_plot: setting tick locations (with pp_plot_maketicks), instead of using plot's default" $
: "pp_plot: setting tick locations (with pp_plot_decideintervals), instead of using plot's default"
;Get the syle flags
dexact=(dstyle and 1) eq 1
dextend=(dstyle and 2) eq 2
dzero=(dstyle and 16) eq 16
;Set number of ticks and interval size to match
;Determine the data and plot ranges
dprange=array_equal([0d0,0d0],drange) ? [min(d,max=dmax,/nan),dmax] : drange
if (yaxis && ~dzero && ~dexact) then dprange[0]<=0d0
if (dtickinterval ne 0d0) then begin ;Interval size takes precedence over number
dticks=floor((dprange[1]-dprange[0])/dtickinterval)
dint=dtickinterval
endif else begin
if (dticks eq 0) then begin
if (~decide) then begin ;Old algorithm to decide on tick positions
dticks=3 ;Default number of ticks to use, if interval was also not provided
dint=(dprange[1]-dprange[0])/dticks
endif else begin
pp_plot_decideintervals,dprange,dticks,dint,decide=decide,newrange=newrange,dminor=dminor,newdticks=newdticks;New algorithm
dticks--
endelse
endif
endelse
if (~dexact) then begin
if (~decide) then begin
dprange+=[-dint,dint] ;If range is not exact, add one interval to each side
dint=(dprange[1]-dprange[0])/dticks
endif else begin
dprange=newrange
dticks=newdticks
endelse
endif
if (~decide) then begin
if (dextend) then dprange+=[-dint,dint]*0.5d0 else $;If extend range is set, add a half interval to each side
dtickv=dprange[0]+dindgen(dticks+1)*dint
endif else begin
dtickv=dprange[0]+dindgen(dticks+1)*dint
if (dextend) then dprange+=([-dint,dint]/dminor)*21d0/34d0
endelse
if ~((det and 1) eq 1) then dtickv=dtickv[1:*]
if ~((det and 2) eq 2) then dtickv=dtickv[0:n_elements(dtickv)-2]
dticks=n_elements(dtickv)-1
end
;+
; :Hidden: Intended to be called only by `pp_plot`
; :Description:
; Uses the state of `multiplot_pp` to determine `xendticks` and `yendticks`. Does
; nothing to these values if a valid `multiplot_pp` state is not found.
;
; This code is encapsulated into this routine to keep the code insulate from the
; common variables used by `multiplot_pp`.
;-
pro pp_plot_get_xyendticks,xendticks,yendticks
compile_opt idl2,logical_predicate,hidden
common multiplot_pp $
,nplots $ ; [# of plots along x, # of plots along y]
,nleft $ ; # of plots remaining---like the first element of !p.multi
,pdotmulti $ ; saved value of !p.multi
,margins $ ; calculated margins based on !p.multi or pmulti
,pposition $ ; saved value of !p.position
,colmajor $ ; flag for column major order
,noerase $ ; saved value of !p.noerase
,xtickname $ ; Original value
,ytickname $ ; Original value
,xtickformat $; Original value
,ytickformat,$ ; Original value
ontop,onright ;Whether the current plot will be on the bottom line or the left column
;Get out if the multiplot variables were not defined
if n_elements(ontop) eq 0 then return
;Set x|yendticks according to the plot location on the grid
xendticks=onright ? 3 : 1
yendticks=ontop ? 3 : 1
print,'pp_plot: setting xendticks to ',strtrim(xendticks,2),' and yendticks to ',strtrim(yendticks,2)
end
;+
; :Description:
; Wrapper to IDL's plot procedure, to allow supressing the first and/or last tick values, so
; that they do not overlap with neighbouring plots (particularly if using `multiplot_pp`).
;
; :Params:
; x : in, required
; Passed to IDL's plot, see its help. Used to determine the x range if xrange is not provided (or the
; y range if y is not provided).
; y : in, optional
; Passed to IDL's plot, see its help. Used to determine the y range if yrange is not provided.
;
; :Keywords:
; use_multiplot : in, optional, default=0
; If set, `xendticks` and `yendticks` are determined by the plot's position
; in the grid set by `multiplot_pp`: Plots not on the top row get the top
; y tick suppressed (`yendticks`=1), plots not on the right column get the right
; x tick suppressed (`xendticks`=1).
; calculate_ticks : in, optional, default=0
; If set and tick values are not provided, the tick values are defined here,
; instead of letting IDL's plot do it. Useful to keep plots with ticks suppressed
; (in which case auto tick locations are calculated here) consistent with those
; made with no suppresion (which otherwise would have their ticks determined by IDL's
; plot).
; xendticks : in, optional, default=3
; Its value determines the kind of supression of the end ticks for the x axis:
;
; 0 - No end ticks are printed (both the first and last are supressed).
;
; 1 - Only the last is supressed, the first tick is left untouched.
;
; 2 - Only the first is supressed, the last tick is left untouched.
;
; 3 - No supression, this routine just passes its arguments to plot, with no changes.
; yendticks : in, optional, default=3
; Its value determines the kind of supression of the end ticks for the y axis:
;
; 0 - No end ticks are printed (both the first and last are supressed).
;
; 1 - Only the last is supressed, the first tick is left untouched.
;
; 2 - Only the first is supressed, the last tick is left untouched.
;
; 3 - No supression, this routine just passes its arguments to plot, with no changes.
; _extra : in, optional
; Any other keywords are just passed to plot, unaltered.
; xrange : in, optional
; Passed to IDL's plot, see its help.
; xtickv : in, optional
; Passed to IDL's plot, see its help.
; xticks : in, optional
; Passed to IDL's plot, see its help.
; xtickinterval : in, optional
; Passed to IDL's plot, see its help.
; xstyle : in, optional
; Passed to IDL's plot, see its help.
; xtick_get : out, optional
; Passed to IDL's plot, see its help.
; yrange : in, optional
; Passed to IDL's plot, see its help.
; ytickv : in, optional
; Passed to IDL's plot, see its help.
; yticks : in, optional
; Passed to IDL's plot, see its help.
; ytickinterval : in, optional
; Passed to IDL's plot, see its help.
; ystyle : in, optional
; Passed to IDL's plot, see its help.
; ytick_get : out, optional
; Passed to IDL's plot, see its help.
;
; :Examples:
; To make a plot supressing both end marks on x, but supress only the bottom mark on y::
;
; pp_plot,dindgen(10),dindgen(10)*2,xendticks=0,yendticks=2,xstyle=1,ystyle=1,xticks=5,xminor=4
; ;pp_plot: setting tick locations (with pp_plot_maketicks), instead of using plot's default
; ;pp_plot: setting tick locations (with pp_plot_maketicks), instead of using plot's default
;
; To make multiplots (using `multiplot_pp`, a variation of IDLastro's multiplot) where the end marks do not overlap::
;
; multiplot_pp,[2,2]
; pp_plot,[0,1],[0,1],xstyle=1,ystyle=1,/use_multiplot,/calculate_ticks
; ;pp_plot: setting xendticks to 1 and yendticks to 3
; ;pp_plot: setting tick locations (with pp_plot_maketicks), instead of using plot's default
; ;pp_plot: setting tick locations (with pp_plot_maketicks), instead of using plot's default
; multiplot_pp
; pp_plot,[0,1],[0,1],xstyle=1,ystyle=1,/use_multiplot,/calculate_ticks
; ;pp_plot: setting xendticks to 3 and yendticks to 3
; ;pp_plot: setting tick locations (with pp_plot_maketicks), instead of using plot's default
; ;pp_plot: setting tick locations (with pp_plot_maketicks), instead of using plot's default
; multiplot_pp
; pp_plot,[0,1],[0,1],xstyle=1,ystyle=1,/use_multiplot,/calculate_ticks
; ;pp_plot: setting xendticks to 1 and yendticks to 1
; ;pp_plot: setting tick locations (with pp_plot_maketicks), instead of using plot's default
; ;pp_plot: setting tick locations (with pp_plot_maketicks), instead of using plot's default
; multiplot_pp
; pp_plot,[0,1],[0,1],xstyle=1,ystyle=1,/use_multiplot,/calculate_ticks
; ;pp_plot: setting xendticks to 3 and yendticks to 1
; ;pp_plot: setting tick locations (with pp_plot_maketicks), instead of using plot's default
; ;pp_plot: setting tick locations (with pp_plot_maketicks), instead of using plot's default
;
;
; :Author: Paulo Penteado (pp.penteado@gmail.com), May/2010
;
; :History:
; Added the keywords `use_multiplot` and `calculate_ticks`. Apr/2011.
;-
pro pp_plot,x,y,xendticks=xet,yendticks=yet,_extra=ex, $
xrange=ixrange,xtickv=ixtickv,xticks=xticks,xtickinterval=ixtickinterval,xstyle=xstyle,xtick_get=xtick_get,xminor=xminor, $
yrange=iyrange,ytickv=iytickv,yticks=yticks,ytickinterval=iytickinterval,ystyle=ystyle,ytick_get=ytick_get,yminor=yminor, $
use_multiplot=usemult,calculate_ticks=calcticks
compile_opt idl2
;Defaults
xet=n_elements(xet) eq 1 ? xet : 3
yet=n_elements(yet) eq 1 ? yet : 3
usemult=n_elements(usemult) eq 1 ? usemult : 0B
calcticks=n_elements(calcticks) eq 1 ? calcticks : 0B
;Auto set xendticks and yendticks according to what multiplot_pp is doing
if usemult then pp_plot_get_xyendticks,xet,yet
;Do nothing special if xet and yet are both 3 and no ticks are set here
if ((xet eq 3) && (yet eq 3) && (~ calcticks)) then begin
plot,x,y,_extra=ex, $
xrange=ixrange,xtickv=ixtickv,xticks=xticks,xtickinterval=ixtickinterval,xstyle=xstyle,xtick_get=xtick_get, $
yrange=iyrange,ytickv=iytickv,yticks=yticks,ytickinterval=iytickinterval,ystyle=ystyle,ytick_get=ytick_get
return
endif
;Get the relevant keywords to use
if (xet ne 3)||calcticks then begin
xtickv=n_elements(ixtickv) ne 0 ? double(ixtickv) : !x.tickv
xticks=n_elements(xticks) eq 1 ? xticks : !x.ticks
xtickinterval=n_elements(ixtickinterval) eq 1 ? double(ixtickinterval) : !x.tickinterval
xstyle=n_elements(xstyle) eq 1 ? xstyle : !x.style
xsupress=(xstyle and 4) eq 4
xrange=n_elements(ixrange) eq 2 ? double(ixrange) : !x.range
xt0=array_equal(xtickv,replicate(0,n_elements(xtickv)))
xminor=n_elements(xminor) eq 1 ? xminor : !x.minor
endif
if (yet ne 3)||calcticks then begin
ytickv=n_elements(iytickv) ne 0 ? double(iytickv) : !y.tickv
yticks=n_elements(yticks) eq 1 ? yticks : !y.ticks
ytickinterval=n_elements(iytickinterval) eq 1 ? double(iytickinterval) : !y.tickinterval
ystyle=n_elements(ystyle) eq 1 ? ystyle : !y.style
ysupress=(ystyle and 4) eq 4
yrange=n_elements(iyrange) eq 2 ? double(iyrange) : !y.range
yt0=array_equal(ytickv,replicate(0,n_elements(ytickv)))
yminor=n_elements(yminor) eq 1 ? yminor : !y.minor
endif
;Make the xticks if they were not provided
if (n_elements(y) gt 0) then begin ;If both x and y were provided
;X axis
if (calcticks || (xet ne 3))&&xt0 then $
pp_plot_maketicks,xprange,xrange,double(x),xtickinterval,xticks,xint,xstyle,xtickv,xet,decide=calcticks,dminor=xminor
;Y axis
if (calcticks || (yet ne 3))&&yt0 then $
pp_plot_maketicks,yprange,yrange,double(y),ytickinterval,yticks,yint,ystyle,ytickv,yet,/yaxis,decide=calcticks,dminor=yminor
endif else begin ;If only x is provided, in which case it is interpreted as y, and x is an index array.
;X axis
if (calcticks || (xet ne 3))&&xt0 then $
pp_plot_maketicks,xprange,xrange,dindgen(n_elements(x)),xtickinterval,xticks,xint,xstyle,xtickv,xet,decide=calcticks,dminor=xminor
;Y axis
if (calcticks || (yet ne 3))&&yt0 then $
pp_plot_maketicks,yprange,yrange,double(x),ytickinterval,yticks,yint,ystyle,ytickv,yet,/yaxis,decide=calcticks,dminor=yminor
endelse
if calcticks||(((xet ne 3) && (yet ne 3))) then $ ;If both x and y ticks were set here
plot,x,y,_extra=ex, $
xrange=n_elements(xprange) eq 2 ? xprange : xrange,xtickv=xtickv,xticks=xticks,xtickinterval=xtickinterval,xtick_get=xtick_get,xminor=xminor,xstyle=(xstyle or 1) and (not 2), $
yrange=n_elements(yprange) eq 2 ? yprange : yrange,ytickv=ytickv,yticks=yticks,ytickinterval=ytickinterval,ytick_get=ytick_get,yminor=yminor,ystyle=(ystyle or 1) and (not 2)
if ((xet ne 3) && (yet eq 3)) then $ ;If only x ticks were set here
plot,x,y,_extra=ex, $
xrange=n_elements(xprange) eq 2 ? xprange : xrange,xtickv=xtickv,xticks=xticks,xtickinterval=xtickinterval,xtick_get=xtick_get,xminor=xminor,xstyle=(xstyle or 1) and (not 2), $
yrange=iyrange,ytickv=iytickv,yticks=yticks,ytickinterval=iytickinterval,ystyle=ystyle,ytick_get=ytick_get
if ((xet eq 3) && (yet ne 3)) then $ ;If only y ticks were set here
plot,x,y,_extra=ex, $
xrange=ixrange,xtickv=ixtickv,xticks=xticks,xtickinterval=ixtickinterval,xstyle=xstyle,xtick_get=xtick_get, $
yrange=n_elements(yprange) eq 2 ? yprange : yrange,ytickv=ytickv,yticks=yticks,ytickinterval=ytickinterval,ytick_get=ytick_get,yminor=yminor,ystyle=(ystyle or 1) and (not 2)
end