; gmwindow object ; Greg Michael 2008-2015 ; ; purpose ; To produce high-quality anti-aliased screen plots using a syntax as close as possible to the standard IDL ; direct graphics commands, and to generate identical screen and postscript output from the same ; command sequence. ; ; requirements ; frebin() - from IDL Astronomy User's Library at http://idlastro.gsfc.nasa.gov/ ; gm_oploterr - modified IDL library routine (added _extra keyword) ; ; initialisation ; Obj = OBJ_NEW('gmwindow', [keywords as for WINDOW command] [,factor=value] [, ppcm=value] [,pt_size=value] [,pt_symsize=value] [,filename=value]) ; ; keywords as for direct graphics "window" command except: ; factor - anti-aliasing factor. Object creates a plot 'factor' times larger than required for display. The reduction of this image to display ; size produces intermediate pixel values on hard edges, giving a smoother appearance. Higher values give better quality. Default ; value is 4. ; ppcm - pixels/cm - if set, xsize,ysize understood in cm instead of pixels; screen image will have dimensions [xsize,ysize]*ppcm ; - allows simple equivalence between ps and screen plots ; pt_size - in conjunction with ppcm, allows the standard charsize (charsize=1) to be redefined to be a specific point size ; pt_symsize - like pt_size, but for plot symbols ; filename - postscript output filename ; supports following standard direct graphics commands as methods: ; erase, arrow, textwidth(), xyouts, oplot, plot, axis, tv, tvscl, oploterr ; ; optionally supports these additional direct graphics routines from David Fanning: ; symcat() (requires modified version, gm_symcat() with added _extra keyword), histoplot ; ; ; additional keywords to graphics command methods ; no_draw - set to prevent redraw of anti-aliased window (can speed up rendering when there any many overplots) ; ; additional methods ; save - for ps, save and close file; for screen, send to png file ; draw - redraw anti-aliased window (required only if image constructed with no_draw keyword set) ; ; ; ; ;Example 1: Create simple screen plot ; ;device,decomposed=0 ;loadct,0 ;w=obj_new("gmwindow",title="gmwindow",xsize=600,ysize=400) ;w->plot,indgen(20),findgen(20)^2,psym=-5,ytitle="an axis title",background=255,color=0,charsize=2 ;w->oplot,indgen(20),400-findgen(20)^2,psym=-4,color=128 ;w->xyouts,5,200,"a label",color=0,charsize=3; ;obj_destroy,w ; ;Note that after the object declaration, syntax is *exactly* as standard IDL except for the initial w-> ; ; ; ; ;Example 2: Create replica screen and postscript plots ; ;Two files are created, "example2.png" and "example2.eps" with identical format plots usings lines, symbols (also ;a custom symbol from symcat), text labels and an image. The postscipt image will have dimensions 6x4cm; the screen ;image will be 600x400 pixels. The standard font size is set at 6 points. ; ; ;pro Example2,output_folder ; device,decomposed=0 ; loadct,0 ; for ps=0,1 do begin ; ;when ps=0 make screen plot, when ps=1 do postscript... ; w=obj_new("gmwindow",xsize=6,ysize=4,ppcm=100,pt_size=6,filename=output_folder+"example2",ps=ps) ; ; w->plot,indgen(20),findgen(20)^2,psym=-5,ytitle="an axis title",background=255,color=0 ; w->oplot,indgen(20),400-findgen(20)^2,psym=-(w->symcat(11)),color=128 ; w->xyouts,5,200,"a label",color=0,charsize=1.5 ; w->tvscl,dist(50),10,10 ; w->save ; obj_destroy,w ; endfor ;end ;Example2,"d:\mydocs\tmp\" ; ;----------------------------------------------------------------------------------------------------------------------- ;Copyright (c) 2010, Greg Michael ;All rights reserved. ; ;Redistribution and use in source and binary forms, with or without modification, are permitted ;provided that the following conditions are met: ; ; 1. Redistributions of source code must retain the above copyright notice, this list of ; conditions and the following disclaimer. ; ; 2. Redistributions in binary form must reproduce the above copyright notice, this list of ; conditions and the following disclaimer in the documentation and/or other materials ; provided with the distribution. ; ;THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ;ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ;WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ;DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ;ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ;(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS ;OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ;THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ;NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN ;IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ;----------------------------------------------------------------------------------------------------------------------- ;Modifications ;2013-12-05 Replaced "font=1" in graphics function calls for "!p.font=1" for screen gmwindow obj.; was causing PS text to be rendered as polygons function gmw_frebin3,im,nl,ns,nb out=fltarr(nl,ns,nb) for i=0,nb-1 do out[*,*,i]=frebin(im[*,*,i],nl,ns) return,out end function gmwindow::pt2charsize,pt_size ;return,pt_size*(self.ps?.000123:.0041)*self.ppcm ;calibration from point size to screen/postcript font sizes ;return,pt_size*(self.ps?8.33e-5:.00353)*self.ppcm ;calibration from point size to screen/postcript font sizes 2013-12-16 return,pt_size*(self.ps?.00011:.00353)*self.ppcm ;calibration from point size to screen/postcript font sizes 2016-01-05 end pro gmwindow::erase,Background_Color,no_draw=no_draw,_extra=_extra wset,self.pixmap_index case n_elements(Background_Color) of 1:erase,Background_Color,_extra=_extra 0:erase,_extra=_extra endcase if ~self.ps and ~self.image then wset,self.index if ~keyword_set(no_draw) then self->draw end ; ; comment out so that .sav file doesn't become huge (IDL 8.1; fixed 8.2+patch) ; pro gmwindow::arrow,x0,y0,x1,y1,no_draw=no_draw,_extra=_extra case self.ps of 0:wset,self.pixmap_index 1: endcase if n_elements(thick) eq 0 then thick=1. arrow,x0,y0,x1,y1,thick=thick*self.thick*self.f,_extra=_extra if ~self.ps and ~self.image then wset,self.index if ~keyword_set(no_draw) then self->draw end function gmwindow::textwidth,text,charsize=charsize0,pt_size=pt_size,char_height=char_height,cm=cm,device=device ;return width in normal coords, unles /cm set - then in cm charsize=n_elements(charsize0) eq 0?1.:charsize0 charsize*=self.charsize if n_elements(pt_size) gt 0 then charsize=self->pt2charsize(pt_size) case self.ps of 0:begin window,/free,xsize=self.x,ysize=300,/pixmap xyouts,0,150,width=w,strjoin(text,'!c'),charsize=charsize,/device wdelete char_height=charsize*float(!d.y_ch_size)/self.y ;(fraction of window height) end 1:begin xyouts,0,-500,width=w,strjoin(text,'!c'),charsize=charsize,color=0,/device ;out of plot area char_height=charsize*float(!d.y_ch_size)/self.y ;(fraction of window height) end endcase case 1 of keyword_set(cm):begin char_height*=self.x/self.ppcm return,w*self.x/self.ppcm end keyword_set(device):begin char_height*=self.x return,w*self.x end else:return,w ;(fraction of window width - /normal) endcase end pro gmwindow::xyouts,x,y,s,charsize=charsize0,pt_size=pt_size,device=device,width=width,no_draw=no_draw,_extra=_extra charsize=n_elements(charsize0) eq 0?1.:charsize0 charsize*=self.charsize if n_elements(pt_size) gt 0 then charsize=self->pt2charsize(pt_size) if ~self.ps then wset,self.pixmap_index ; if keyword_set(device) then xyouts,x*self.f,y*self.f,s,font=1,charsize=charsize*self.f,/device,_extra=_extra $ ; else xyouts,x,y,s,font=1,charsize=charsize*self.f,_extra=_extra case keyword_set(device) of 1:xyouts,x*self.f,y*self.f,s,charsize=charsize*self.f,/device,width=width,_extra=_extra 0:xyouts,x,y,s,charsize=charsize*self.f,width=width,_extra=_extra endcase if ~self.ps and ~self.image then wset,self.index if ~keyword_set(no_draw) then self->draw end pro gmwindow::oploterr,x,y,err,err_minus,psym,symsize=symsize,thick=thick,no_draw=no_draw,_extra=_extra if n_elements(thick) eq 0 then thick=1. if n_elements(symsize) eq 0 then symsize=1. if ~self.ps then wset,self.pixmap_index gm_oploterr,x,y,err,err_minus,psym,thick=thick*self.thick*self.f,symsize=symsize*self.f,_extra=_extra if ~self.ps and ~self.image then wset,self.index if ~keyword_set(no_draw) then self->draw end pro gmwindow::oplot,x,y,symsize=symsize0,thick=thick,no_draw=no_draw,_extra=_extra symsize=n_elements(symsize0) eq 0.?1.:symsize0 symsize*=self.symsize if n_elements(thick) eq 0 then thick=1. if ~self.ps then wset,self.pixmap_index if n_elements(y) gt 0 then oplot,x,y,thick=thick*self.thick*self.f,symsize=symsize*self.f,_extra=_extra $ else oplot,x,thick=thick*self.thick*self.f,symsize=symsize*self.f,_extra=_extra if ~self.ps and ~self.image then wset,self.index if ~keyword_set(no_draw) then self->draw end pro gmwindow::plot,x,y,charsize=charsize0,pt_size=pt_size,thick=thick,xthick=xthick,ythick=ythick,symsize=symsize0,position=position,xtick_get=xtick_get,no_draw=no_draw,_extra=_extra ;if pt_size given, overrides charsize charsize=n_elements(charsize0) eq 0.?1.:charsize0 charsize*=self.charsize symsize=n_elements(symsize0) eq 0.?1.:symsize0 symsize*=self.symsize if n_elements(pt_size) gt 0 then charsize=self->pt2charsize(pt_size) if n_elements(thick) eq 0 then thick=1. if n_elements(xthick) eq 0 then xthick=1. if n_elements(ythick) eq 0 then ythick=1. if n_elements(position) gt 0 then begin position1=position if total(tag_names(_extra) eq 'DEVICE') then if _extra.device then position1*=self.f ;2015-10-13 changed to _ref_extra endif if ~self.ps then wset,self.pixmap_index case n_elements(y) gt 0 of 0:plot,x,position=position1,charsize=charsize*self.f,thick=thick*self.thick*self.f,xthick=xthick*self.thick*self.f,ythick=ythick*self.thick*self.f,symsize=symsize*self.f,xtick_get=xtick_get,_extra=_extra ;font=1, 1:plot,x,y,position=position1,charsize=charsize*self.f,thick=thick*self.thick*self.f,xthick=xthick*self.thick*self.f,ythick=ythick*self.thick*self.f,symsize=symsize*self.f,xtick_get=xtick_get,_extra=_extra ;font=1, endcase if ~self.ps and ~self.image then wset,self.index if ~keyword_set(no_draw) then self->draw end pro gmwindow::plots,x,y,thick=thick,symsize=symsize0,no_draw=no_draw,_extra=_extra symsize=n_elements(symsize0) eq 0.?1.:symsize0 symsize*=self.symsize if n_elements(thick) eq 0 then thick=1. if ~self.ps then wset,self.pixmap_index f=_extra.device?self.f:1 case n_elements(y) gt 0 of 0:plots,x*f,thick=thick*self.thick*self.f,symsize=symsize*self.f,_extra=_extra 1:plots,x*f,y*f,thick=thick*self.thick*self.f,symsize=symsize*self.f,_extra=_extra endcase if ~self.ps and ~self.image then wset,self.index if ~keyword_set(no_draw) then self->draw end function gmwindow::symcat,index,thick=thick if n_elements(thick) eq 0 then thick=1. return,gm_symcat(index,thick=thick*self.thick*self.f) end pro gmwindow::polyfill,x,y,no_draw=no_draw,_extra=_extra if ~self.ps then wset,self.pixmap_index polyfill,x,y,_extra=_extra if ~self.ps and ~self.image then wset,self.index if ~keyword_set(no_draw) then self->draw end pro gmwindow::histoplot,data,charsize=charsize0,pt_size=pt_size,thick=thick,xthick=xthick,ythick=ythick,symsize=symsize,position=position,gm=gm,no_draw=no_draw,_extra=_extra ;if pt_size given, overrides charsize charsize=n_elements(charsize0) eq 0?1.:charsize0 charsize*=self.charsize if n_elements(pt_size) gt 0 then charsize=self->pt2charsize(pt_size) if n_elements(thick) eq 0 then thick=1. if n_elements(xthick) eq 0 then xthick=1. if n_elements(ythick) eq 0 then ythick=1. if n_elements(symsize) eq 0 then symsize=1. if n_elements(position) gt 0 then begin position1=position if total(tag_names(_extra) eq 'DEVICE') then if _extra.device then position1*=self.f endif if ~self.ps then wset,self.pixmap_index case keyword_set(gm) of 0:histoplot,data,position=position1,charsize=charsize*self.f,thick=thick*self.thick*self.f,xthick=xthick*self.thick*self.f,ythick=ythick*self.thick*self.f,symsize=symsize*self.f,_extra=_extra ;font=1, ;modified version of D Fanning's histoplot for special purpose. Normally not required. 1:gm_histoplot,data,position=position1,charsize=charsize*self.f,thick=thick*self.thick*self.f,xthick=xthick*self.thick*self.f,ythick=ythick*self.thick*self.f,symsize=symsize*self.f,_extra=_extra ;font=1, endcase if ~self.ps and ~self.image then wset,self.index if ~keyword_set(no_draw) then self->draw end pro gmwindow::axis,x,y,charsize=charsize0,pt_size=pt_size,xthick=xthick,ythick=ythick,no_draw=no_draw,_extra=_extra ;if pt_size given, overrides charsize charsize=n_elements(charsize0) eq 0?1.:charsize0 charsize*=self.charsize if n_elements(pt_size) gt 0 then charsize=self->pt2charsize(pt_size) if n_elements(thick) eq 0 then thick=1. if n_elements(xthick) eq 0 then xthick=1. if n_elements(ythick) eq 0 then ythick=1. if ~self.ps then wset,self.pixmap_index case (n_elements(x) gt 0)+(n_elements(y) gt 0) of 0:axis,charsize=charsize*self.f,xthick=xthick*self.thick*self.f,ythick=ythick*self.thick*self.f,_extra=_extra ;font=1, 1:axis,x,charsize=charsize*self.f,xthick=xthick*self.thick*self.f,ythick=ythick*self.thick*self.f,_extra=_extra 2:axis,x,y,charsize=charsize*self.f,xthick=xthick*self.thick*self.f,ythick=ythick*self.thick*self.f,_extra=_extra endcase if ~self.ps and ~self.image then wset,self.index if ~keyword_set(no_draw) then self->draw end pro gmwindow::tv,image,p1,p2,p3,no_draw=no_draw,_extra=_extra if ~self.ps then wset,self.pixmap_index sz=size(image,/dim) sz_cm=sz/self.ppcm_screen q=n_elements(sz) eq 3?where(sz ne 3):[0,1] sz[q]*=self.f im=rebin(image,sz) ;should manage true colour as well case (n_elements(p1) gt 0)+(n_elements(p2) gt 0)+(n_elements(p3) gt 0)+self.ps*4 of ;screen cases 0:tv,im,_extra=_extra 1:tv,im,p1,_extra=_extra 2:tv,im,p1*self.f,p2*self.f,_extra=_extra 3:tv,im,p1*self.f,p2*self.f,p3,_extra=_extra ;ps versions 4:tv,im,_extra=_extra 5:tv,im,p1,_extra=_extra 6:tv,im,p1/self.ppcm_screen,p2/self.ppcm_screen,/centimeters,xsize=sz_cm[0],ysize=sz_cm[1],_extra=_extra 7:tv,im,p1/self.ppcm_screen,p2/self.ppcm_screen,p3,/centimeters,xsize=sz_cm[0],ysize=sz_cm[1],_extra=_extra endcase if ~self.ps and ~self.image then wset,self.index if ~keyword_set(no_draw) then self->draw end pro gmwindow::tvscl,image,p1,p2,p3,no_draw=no_draw,_extra=_extra if ~self.ps then wset,self.pixmap_index sz=size(image,/dim) sz_cm=sz/self.ppcm_screen q=n_elements(sz) eq 3?where(sz ne 3):[0,1] sz[q]*=self.f im=rebin(image,sz) ;should manage true colour as well case (n_elements(p1) gt 0)+(n_elements(p2) gt 0)+(n_elements(p3) gt 0)+self.ps*4 of ;screen cases 0:tvscl,im,_extra=_extra 1:tvscl,im,p1,_extra=_extra 2:tvscl,im,p1*self.f,p2*self.f,_extra=_extra 3:tvscl,im,p1*self.f,p2*self.f,p3,_extra=_extra ;ps versions 4:tvscl,im,_extra=_extra 5:tvscl,im,p1,_extra=_extra 6:tvscl,im,p1/self.ppcm_screen,p2/self.ppcm_screen,/centimeters,xsize=sz_cm[0],ysize=sz_cm[1],_extra=_extra 7:tvscl,im,p1/self.ppcm_screen,p2/self.ppcm_screen,p3,/centimeters,xsize=sz_cm[0],ysize=sz_cm[1],_extra=_extra endcase if ~self.ps and ~self.image then wset,self.index if ~keyword_set(no_draw) then self->draw end pro gmwindow::draw,image=image,mono=mono if self.ps then return if self.image and ~arg_present(image) then return wset,self.pixmap_index case keyword_set(mono) of 0:begin im=tvrd(true=3) im2=gmw_frebin3(im,self.x,self.y,3) end 1:im2=frebin(tvrd(),self.x,self.y) endcase case self.image of 0:begin wset,self.index tv,im2,true=3 end 1:image=temporary(im2) endcase end pro gmwindow::save,filename,factor=factor,mono=mono if n_elements(factor) eq 0 then factor=1 case self.ps of 1:begin device,/close_file set_plot,self.device obj_destroy,self end 0:begin wset,self.pixmap_index ; im=tvrd(true=3) ; im2=gmw_frebin3(im,self.x*factor,self.y*factor,3) case keyword_set(mono) of 0:begin im=tvrd(true=3) im1=gmw_frebin3(im,self.x,self.y,3) im2=transpose(im1,[2,0,1]) end 1:im2=frebin(tvrd(),self.x,self.y) endcase write_png,n_elements(filename) gt 0 ?filename:self.basename+".png",im2 end endcase end function gmwindow::getproperties out={gmwindow} struct_assign,self,out return,out end function gmwindow::init,index,drawID,xsize=xsize,ysize=ysize,factor=f,register=register,$ ps=ps,image=image,filename=filename,ppcm=ppcm,pt_size=pt_size,pt_symsize=pt_symsize,$ _extra=_extra ;ppcm (pixels/cm) - if present, treat xsize,ysize as being in cm like for postscript - to allow screen replica of ps-file; ignore if /ps=1 ;if pt_size set, use to replace default for charsize=1 ;image - return image array only - don't draw - if ps set, overrides this self.image=keyword_set(image) self.ps=keyword_set(ps) self.ppcm_screen=n_elements(ppcm) eq 0?28:ppcm if self.ps then ppcm=1000.; case (n_elements(ppcm) gt 0) of 0:begin self.ppcm=28 self.x=(n_elements(xsize) eq 0)?900:xsize self.y=(n_elements(ysize) eq 0)?600:ysize end 1:begin self.ppcm=ppcm self.x=((n_elements(xsize) eq 0)?9:xsize)*ppcm self.y=((n_elements(ysize) eq 0)?6:ysize)*ppcm end endcase self.f=self.ps?1:(keyword_set(f)?f:4) self.device= !D.NAME if n_elements(index) eq 1 then self.index=index if n_elements(filename) gt 0 then self.basename=filename self.charsize=n_elements(pt_size) eq 1?self->pt2charsize(pt_size):1.5 ;if pt_size set, use for charsize=1 self.symsize=n_elements(pt_symsize) eq 1?self->pt2charsize(pt_symsize)*.7:self.charsize*.5 ;if not set, scale from charsize (IDL ps mode does this by default, but screen mode doesn't) self.thick=self.charsize*(keyword_set(ps)?3.5:1.)*.7 ;factor to compensate thinner PS lines (used to be 2.5... seems to have changed) case keyword_set(ps) of 1:begin set_plot,'PS' !p.font=1 ;set as required case !p.font of ;produce ps output, where text can be edited in Illustrator ...hardware font 0:device,/color,/encapsulated,filename=file_dirname(filename,/mark)+file_basename(filename,".eps")+".eps",$;/helvetica,,/isolatin1*$ xsize=xsize,ysize=ysize,font_size=12;,SET_FONT='Helvetica', /TT_FONT ;produce ps output with TT font, but the text cannot be edited in Illustrator 1:device,/color,/encapsulated,/isolatin1,filename=file_dirname(filename,/mark)+file_basename(filename,".eps")+".eps",$;/helvetica,*$ xsize=xsize,ysize=ysize,font_size=12;,SET_FONT='Helvetica', /TT_FONT endcase self.x=xsize*self.ppcm ;used by text_width() end 0:begin !p.font=1 if ~keyword_set(register) then begin if ~keyword_set(image) then begin case self.index eq 0 of 1:begin window,/free,xsize=self.x,ysize=self.y,_extra=_extra self.index=!d.window end 0:window,self.index,xsize=self.x,ysize=self.y,_extra=_extra endcase endif endif else begin info=widget_info(drawID,/geometry) self.x=info.xsize self.y=info.ysize endelse window,/free,pixmap=1,xsize=self.f*self.x,ysize=self.f*self.y,_extra=_extra self.pixmap_index=!d.window end endcase return,1 end pro gmwindow::cleanup if ~self.ps then wdelete,self.pixmap_index if self.index ne 0 then wdelete,self.index end pro gmwindow__define void={gmwindow, $ x:0L, $ y:0L, $ ppcm:0.,$ ;pixels/cm for current object's device ppcm_screen:0.,$ ;pixels/cm for screen (used to calculate equivalent sizes for tv commands in ps) index:0,$ pixmap_index:0,$ charsize:0.,$ symsize:0.,$ ps:0,$ image:0,$ device:'',$ basename:'',$ thick:0.,$ f:0 $ } end