;+ ; Project : SOHO - CDS ; ; Name : CW_PZOOM() ; ; Purpose : One-window compound widget image zoom. ; ; Explanation : CW_PZOOM is a general purpose image display widget that lets ; the user zoom in and out on an (alterable) image. ; ; The widget is crated in much the same way as a standard ; widget_draw window, although a number of extra keywords are ; used in order to control the appearance of the display (draw ; window size, display window size, title, subtitle, etc.). ; ; SETTING THE IMAGE TO BE DISPLAYED ; ; can be done through the VALUE keyword of CW_PZOOM or through ; the WIDGET_CONTROL SET_VALUE mechanism. The latter should only ; be done after the widget hierarchy has been /realized. Setting ; the value is done by: ; ; widget_control,CW_ID,set_value=image ; ; where image is a two-dimensional array. ; ; The display is automatically refreshed each time the image is ; changed. ; ; CONTROLLING THE COMPOUND WIDGET BEHAVIOUR ; ; The appearance and behaviour of this compound widget is ; controlled by numerous status variables. The status variables ; can be set either through the use of keywords in the ; CW_PZOOM() call, or by using e.g.: ; ; WIDGET_CONTROL,CW_ID,SET_VALUE = STRUCT ; ; where CW_ID is the compound widget ID and STRUCT is a ; structure with one or more tags corresponding to display ; attributes to be altered. To set e.g., the plot TITLE to be ; used: ; ; WIDGET_CONTROL,CW_ID,SET_VALUE = {TITLE:'Zoomable image'} ; ; See the "KEYWORD DEFAULTS/EXPLANATIONS" section in the main ; procedure for an updated list of all the status variables that ; may be set through the use of keywords. If a keyword is not ; marked with a "*" in the comment section, then it is also ; possible to set this status variable through the SET_VALUE ; mechanism. ; ; In addition to the status variables which may be set through ; keywords when creating the widget, there are a few special ; status variables that can ONLY be specified through the ; SET_VALUE mechanism: ; ; XFOCUS : The X index of the focus pixel ; YFOCUS : The Y index of the focus pixel ; ZOOM : The zoom factor. Zoom=1 means all data visible. ; REPLOT : Setting this to 1 causes the display to ; be refreshed, after which it will be cleared. ; RESCALE : Setting this to 1 causes the color scaling to be ; performed and the display to be refreshed, after ; which it will be cleared. Should be set when ; acknowledging an XTVSCALE event. ; ; REPLOT_CROSS : Setting this to 1 causes the crosshair to ; be redrawn, without redisplaying the image. ; ; NO REFRESH IS PERFORMED by set_value when altering ; attributes WITHOUT setting THE REPLOT ATTRIBUTE ; ; EVENT HANDLING ; ; CW_PZOOM can be used in a "dumb" mode through the use of the ; /AUTONOMOUS keyword when creating the widget. This causes the ; widget to acknowledge and gobble up all zooming/refocusing ; events, refreshing the display without passing on events to ; the parent base. ; ; Unless the /AUTONOMOUS keyword is set, all WIDGET_DRAW event ; are processed in the event handler into an "action string", by ; default: ; ; "ZOOM-" for ev.press eq 1 (left button) ; "ZOOMP" for ev.press eq 2 (middle button) ; "ZOOM+" for ev.press eq 4 (right button) ; ; and ; ; "IGNORE" for any other widget_draw event. ; ; The status variable IGNORE_ACTION contains a string with ; a list of action texts to ignore, in the format: ; ; "(IGNORE)(MOTION)(RELEASE1)(RELEASE2)(RELEASE3)" ; ; If an event is translated into an action string that appears ; in the IGNORE_ACTION list, the event is gobbled up and ; ignored. ; ; If you'd like the user to be in control of what buttons to ; use, you can use a CW_MOUSE widget (see keyword CW_MOUSE, and ; the documentation of CW_MOUSE()). The CW_MOUSE actions should ; be: ; ; "ZOOM+" for zooming in (no repointing) ; "ZOOM-" for zooming out (no repointing) ; "ZOOMP" for repointing (or anything else not on the ; IGNORE_ACTION list) ; ; For ZOOM+/ZOOM- actions, the new ZOOM factor is calculated, ; but the pixel coordinates of the button press is ignored. For ; any other action not to be ignored, the event coordinates are ; converted into image pixel coordinates (new XFOCUS/ YFOCUS). ; ; After this, a {CW_PZOOM_EVENT} or {CW_PZOOM_XTVSCALE_EVENT} ; structure is created, and passed on to the owner of the ; compound widget. Note that NO SCREEN OR STATUS UPDATES have ; been done at this stage (see "acknowledging events" below). ; ; The two possible event structures generated by CW_PZOOM ; consist of the following tags: ; ; ID : Widget ID of cw_pzoom ; TOP : Top widget ID. ; HANDLER : Handler widget ID ; XTVSCALE : Signals an XTVSCALE event when nonzero (i.e., ; the event originates from XTVSCALE, not from the ; widget_draw window). ; SET : A structure with new information generated ; by the user pressing a mouse button. The ; tags of this structure {CW_PZOOM_SET} are: ; ; XFOCUS : X index of new/current selected pixel. ; YFOCUS : Y index of new/current selected pixel. ; ZOOM : The new/current zoom factor. ; RESCALE : Set to one when an XTVSCALE event occurred ; REPLOT : Always has a value of 1 ; ; OLD : Contains the same tags as SET, but with ; the old (currently displayed) values. ; ; PLOTREG : The Plot Region that has been used to ; display the image. Useful for overplotting ; after executing PRESTORE,EVENT.PLOTREG ; ; EVENT : The original WIDGET_DRAW event (for ; {CW_PZOOM_EVENT}) or an XTVSCALE_EVENT (for ; {CW_PZOOM_XTVSCALE_EVENT}. ; ; ; ACKNOWLEDGING EVENTS ; ; In order to acknowledge the event to make the user changes ; effective, all that has to be done is to use: ; ; WIDGET_CONTROL,EV.ID,SET_VALUE=EV.SET ; ; this is in fact all that is done when the autonomous mode is ; used. ; ; OVERPLOTTING THE DISPLAY ; ; Since the acknowledgement of a zoom/repointing event, or ; changing the displayed data causes the display to be updated, ; overplotting should be done AFTER setting the focus and/or ; zoom values with REPLOT set to 1. Replotting changes the ; current data coordinate system so overplotting may be done in ; data coordinates. ; ; Useful entities for overplotting can be retrieved through a ; call to WIDGET_CONTROL,CW_ID,GET_VALUE=STATUS, where CW_ID is ; the CW_PZOOM widget ID, and STATUS will be returned as a ; structure with the following tags {CW_PZOOM_VALUE}: ; ; VALUE : HANDLE that points to the data (image) ; Note that the data must not be removed or ; altered directly! ; CLIP : 4-element array with the indices of the lower ; left and upper right pixels that are ; displayed. Note that the display may cover ; pixels outside the actual data array. ; XFOCUS : The X index of the focus pixel ; YFOCUS : The Y index of the focus pixel ; PLOTREG: The plot region used to display the image. ; ; ; A note on XTICKS: ; ; The use of XTICKS is somewhat tricky. The standard IDL way of ; interpreting this keyword normally gives nonsensical results, ; so CW_PZOOM tries to make the tickmarks fall on the center of ; the pixels (which looks good for _some_ types of data). It is ; also possible to use the function TICK_VEC by setting XTICKS ; to a negative value. TICK_VEC tries to do a decent job of ; placing UP TO the given number of tickmarks on the ; display. Try it. ; ; ; Use : PZOOM = CW_PZOOM(BASE [,KEYWORDS]) ; ; Inputs : BASE : The base to put the draw window on. ; ; Opt. Inputs : None. ; ; Outputs : Returns the widget ID of the compound widget. ; ; Opt. Outputs: None. ; ; Keywords : Too many to justify updating a separate list. See the KEYWORD ; DEFAULTS section inside the routine. ; ; Calls : cdscongrid(), clipbox, copy_tag_values, cw_pzoom_plot, ; cw_pzoom_scaleval, datatype(), default, handle_killer_hookup, ; parcheck, pconvert(), prestore, pstore(), tick_vec(), typ(), ; xtvscale() ; ; Common : None. ; ; Restrictions: Probably too many to specify. ; ; Side effects: Updating the !P,!X,!Y system variables when refreshing the ; display. ; ; Category : Utility, Image. ; ; Prev. Hist. : Long. ; ; Written : Stein Vidar Hagfors Haugan, UiO, 13 June 1996 ; ; Modified : Version 2, 16 June 1996 ; Clarified the problem with having two different ; event structures, and specified this explicitly ; in the explanation section. ; Version 3, SVHH, 1 July 1996 ; Added /CLEAN switch to PSTORE(), to delete any ; previous use of the same window number. ; Version 4, SVHH, 29 May 1997 ; Modified grabbing procedure to save time when zoom<1, ; and removed common block as cache. Using ; handle_killer_hookup instead of cleanup routine. ; Version 5, SVHH, 15 September 1997 ; Modified automatic handling of max-zoom limit to make ; more sense. ; ; Version : 5, 15 September 1997 ;- ; ; Scaling the image into a byte array ; PRO cw_pzoom_scaleval,info handle_value,info.value,value,/no_copy use_xtvscale = info.ext.xtvscale GE 0L IF use_xtvscale THEN BEGIN scaledval = xtvscale(info.ext.xtvscale,value, $ missing=missing,color_missing=color_missing) info.ext.missing = missing info.ext.color_missing = color_missing END ELSE BEGIN good = value NE info.ext.missing ixgood = WHERE(good,count_good) ixbad = WHERE(good-1b,count_bad) IF count_bad EQ 0 THEN BEGIN ;; All good scaledval = byte(!D.TABLE_SIZE* $ (VALUE-MIN(VALUE))/(MAX(VALUE)-MIN(VALUE))) END ELSE IF count_good EQ 0 THEN BEGIN ;; All bad scaledval = REPLICATE(byte(info.ext.color_missing), $ info.int.asize(0),info.int.asize(1)) END ELSE BEGIN ;; Some good, some bad. maxdata = MAX(value(ixgood)) mindata = MIN(value(ixgood)) scaledval = byte(!D.table_size* $ (value-mindata)/(maxdata-mindata)) scaledval(ixbad) = info.ext.color_missing END END handle_value,info.value,value,/set,/no_copy handle_value,info.scaledv,scaledval,/set,/no_copy info.ext.rescale = 0 END ; ; Update display ; PRO cw_pzoom_plot,info ;; KEEP !X, !Y, !P OLD_X = !X OLD_Y = !Y OLD_P = !P ;; Rescale colors if necessary IF info.ext.rescale NE 0 THEN BEGIN cw_pzoom_scaleval,info END handle_value,info.scaledv,scaledval,/no_copy handle_value,info.im_h,im,/no_copy ;; Making the array for the display image IF NOT info.ext.squarepix THEN BEGIN ;; Relative physical size of pixels is given by SCALE x_bin = DOUBLE(info.ext.scale(0))/MIN(info.ext.scale) ;; binfactors y_bin = DOUBLE(info.ext.scale(1))/MIN(info.ext.scale) END ELSE BEGIN ;; Pixels to be treated as square x_bin = 1.0D y_bin = 1.0D END ;; Data size (could've used info.int.asize..) dsz = SIZE(scaledval) IF info.ext.stretch THEN BEGIN ;; The size of the grabbed box is calculated from the ;; (array) size of the supplied image, and then ;; stretched to fit the display size. ;; ZOOM == 1 means displayed image should contain all pixels ;; in both directions (but not necessarily shown whith those ;; proportions -- depends on xdsize/ydsize). ;; ;; The SCALE factors thus have no influence on the pixel sizes xnpix = round(dsz(1)/info.ext.zoom) ynpix = round(dsz(2)/info.ext.zoom) END ELSE BEGIN ;; If non-stretch mode is set, the grab box size is calculated as to ;; avoid deformation when rebinning to display size. ;; This enables the possibility of having a viewing window that does ;; *not* have the same proportional size as the data being viewed. ;; When zoom is equal to 1, all data should fit into the display. ;; The scaling factor must take into account the ratios of physical ;; image sizes to display sizes, and make sure that the dimension with ;; the larger ratio fits at zoom=1. ;; ;; Relative physical sizes of the two data dimensions phys_sizes = dsz(1:2)*[x_bin,y_bin] ;; Ratios -- this is how much physical size we must grab for each ;; display pixel in each direction in order to make exact fits ;; for each separate dimension phys_to_disp = phys_sizes/info.int.dsize ;; The displayed pixels are assumed square, so to get a physical ;; area with height/width ratio equal to the height/width of ;; the display, the physical grab size is a mutiple of the ;; display size in each dimension. We multiply with the ;; maximum phys_to_disp ratio to get the most tight dimension ;; to fit at zoom=1. get_size = [info.int.dsize]*MAX(phys_to_disp)/info.ext.zoom ;; Conversion of physical grab sizes to pixel sizes: xnpix = round(get_size(0)/x_bin) ynpix = round(get_size(1)/y_bin) END IF info.ext.zoom GT 1 THEN dsz(1:2) = [xnpix,ynpix] $ ELSE dsz(1:2) = [info.int.dsize] IF info.ext.replot THEN BEGIN ;; Since we're using scaled values we only need byte values dsz(3) = 1 dsz(4) = dsz(1)*dsz(2) back = info.ext.background IF back EQ -1L THEN back = !P.background IF total(SIZE(im) EQ dsz) NE 5 THEN BEGIN ;; Free space im = 0 im = make_array(SIZE = dsz,value=back) END ELSE BEGIN im(*,*) = back END END ;; Renormalizing focus coordinates (just checking) info.ext.xfocus = (info.ext.xfocus > 0) < (info.int.asize(0)-1) info.ext.yfocus = (info.ext.yfocus > 0) < (info.int.asize(1)-1) glued = info.ext.zoom le 1.0 ;; Finding the grab box startx = (info.ext.xfocus-xnpix/2) starty = (info.ext.yfocus-ynpix/2) ;; Centered view -- all or more than all data visible IF glued THEN BEGIN startx = -FIX((xnpix-info.int.asize(0))*0.5) starty = -FIX((ynpix-info.int.asize(1))*0.5) END IF info.ext.zoom EQ 2 THEN BEGIN ;; A compromize between glued/focus-centered position startx = round((2.0*startx - (xnpix-info.int.asize(0))*0.5)/3.0) starty = round((2.0*starty - (ynpix-info.int.asize(1))*0.5)/3.0) END offsetx = 0 offsety = 0 IF startx LT 0 THEN BEGIN offsetx = -startx startx = 0 ENDIF IF starty LT 0 THEN BEGIN offsety = -starty starty = 0 ENDIF IF info.ext.zoom LE 1 THEN BEGIN ;; All data visible... xsize = round(float(info.int.asize(0))*info.int.dsize(0)/xnpix + 0.25) ysize = round(float(info.int.asize(1))*info.int.dsize(1)/ynpix + 0.25) xstart = round(float(offsetx)*info.int.dsize(0)/xnpix) ystart = round(float(offsety)*info.int.dsize(1)/ynpix) im(xstart,ystart) = cdscongrid(scaledval,xsize,ysize) END endx = (startx+xnpix-1-offsetx) < (info.int.asize(0)-1) endy = (starty+ynpix-1-offsety) < (info.int.asize(1)-1) IF info.ext.zoom GT 1 THEN BEGIN IF info.ext.replot THEN BEGIN im(offsetx,offsety) = scaledval(startx:endx,starty:endy) ;; grab plim = cdscongrid(im,info.int.dsize(0),info.int.dsize(1)) END END ELSE BEGIN plim = temporary(im) ;; No memory copying END INFO.int.zero = [startx-offsetx, starty-offsety,$ startx-offsetx+xnpix-1,starty-offsety+ynpix-1] IF info.ext.replot THEN BEGIN !X.range = (info.ext.origin(0) + $ info.ext.scale(0)*(info.int.zero(0)+[-0.5,xnpix-0.5])) !Y.range = (info.ext.origin(1) + $ info.ext.scale(1)*(info.int.zero(1)+[-0.5,ynpix-0.5])) position = [info.ext.origo,info.ext.origo + info.int.dsize-1]+0.00001 origo = info.ext.origo IF !d.name EQ 'PS' THEN BEGIN !P.noerase = 1 position = [!P.clip(0),!p.clip(1),!P.clip(2),!P.clip(3)] xsize = !P.clip(2)-!P.clip(0) ysize = !P.clip(3)-!P.clip(1) origo = !P.clip(0:1) END ELSE BEGIN xsize = 0 ysize = 0 wset,info.int.tv END blank = STRING(255b) ;; Shorthand e = info.ext IF e.background NE -1L THEN !P.background = e.background IF e.charsize NE -1.0 THEN !P.charsize = e.charsize IF e.xcharsize NE -1.0 THEN !X.charsize = e.xcharsize IF e.ycharsize NE -1.0 THEN !Y.charsize = e.ycharsize IF e.charthick NE -1.0 THEN !P.charthick = e.charthick IF e.color NE -1L THEN !P.color = e.color IF e.font NE -2L THEN !P.font = e.font IF e.xgridstyle NE -1L THEN !X.gridstyle = e.xgridstyle IF e.ygridstyle NE -1L THEN !Y.gridstyle = e.ygridstyle IF e.xstyle NE -1L THEN !X.style = e.xstyle IF e.ystyle NE -1L THEN !Y.style = e.ystyle IF e.subtitle NE blank THEN !P.subtitle = e.subtitle IF e.xthick NE -1.0 THEN !X.thick = e.xthick IF e.ythick NE -1.0 THEN !Y.thick = e.ythick IF e.xtickformat NE blank THEN !X.tickformat = e.xtickformat IF e.ytickformat NE blank THEN !Y.tickformat = e.ytickformat IF e.xticks NE 0 THEN !X.ticks = e.xticks IF e.yticks NE 0 THEN !Y.ticks = e.yticks IF e.ticklen NE 0.0 THEN !P.ticklen = e.ticklen IF e.xticklen NE 0.0 THEN !X.ticklen = e.xticklen IF e.yticklen NE 0.0 THEN !Y.ticklen = e.yticklen IF e.title NE blank THEN !P.title = e.title IF e.xtitle NE blank THEN !X.title = e.xtitle IF e.ytitle NE blank THEN !Y.title = e.ytitle ;; Xticks are special IF e.xticks LT 0 THEN BEGIN xtickv = tick_vec(-info.ext.xticks, $ !X.range(0),!X.range(1), $ subticks=xminor) !x.ticks = N_ELEMENTS(xtickv)-1 !X.tickv = xtickv !X.minor = xminor END ELSE IF info.ext.xticks GT 0 THEN BEGIN ;; Try to put the xticks on the center of pixels nticks = (info.ext.xticks+1) < xnpix !X.ticks = nticks-1 xticki = (LINDGEN(nticks)*xnpix)/nticks + xnpix/(2*nticks) !X.tickv = (info.ext.origin(0) + $ info.ext.scale(0)*(xticki+info.int.zero(0))) !X.minor = 0 END !X.style = 1 OR !x.style !y.style = 1 OR !y.style ;; Plotting ;; plot,[0],/nodata,position=position,/DEVICE tv,plim,origo(0),origo(1),xsize=xsize,ysize=ysize,/DEVICE clipbox ;; Store coordinate system etc. ;; OLD_P.clip = !P.clip OLD_P.position = !P.position OLD_X.window = !X.window OLD_X.type = !X.type OLD_X.crange = !X.crange OLD_X.s = !X.s OLD_Y.window = !Y.window OLD_Y.type = !Y.type OLD_Y.crange = !Y.crange OLD_Y.s = !Y.s !P = OLD_P !X = OLD_X !Y = OLD_Y dummy = pstore(1,xnpix,ynpix,/clean) IF !D.name NE 'PS' THEN info.int.preg = dummy info.ext.replot_cross = 1 END ELSE BEGIN dummy = info.int.preg ;; Replot CROSS? END IF info.ext.zoom LE 1 THEN handle_value,info.im_h,plim,/set,/no_copy $ ELSE handle_value,info.im_h,im,/set,/no_copy prestore,dummy ;; ;; Plot crosshair ;; ;; ;; Fractional pixel errors in the display ;; xfracp = round(0.49999d*xnpix/double(info.int.dsize(0))) yfracp = round(0.49999d*ynpix/double(info.int.dsize(1))) xx = pconvert(dummy,info.ext.xfocus-info.int.zero(0)+xfracp,$ /pixel,/to_device) yy = pconvert(dummy,info.ext.yfocus-info.int.zero(1)+yfracp,$ /pixel,/to_device,/Y) xx = round(xx) yy = round(yy) IF info.ext.replot_cross AND info.ext.cross GT 0.0 THEN BEGIN cross = info.ext.cross IF !D.name NE 'PS' THEN BEGIN DEVICE,get_graphics_function=oldgraph DEVICE,set_graphics_function=info.ext.cross_graph END ELSE BEGIN ;; Expand cross /DEVICE size corresponding to the change ;; in device clip size for POSTSCRIPT output cross = cross* $ MIN([(!P.clip(2)-!P.clip(0))/FLOAT(info.int.dsize(0)), $ (!P.clip(3)-!P.clip(1))/FLOAT(info.int.dsize(1))]) END color = info.ext.cross_color IF color EQ -1L THEN color = info.ext.color IF color EQ -1L THEN color = !P.color PLOTS,[xx,xx],yy+cross*[-1,1],/DEVICE,color=color PLOTS,xx+cross*[-1,1],[yy,yy],/DEVICE,color=color IF !D.name NE 'PS' THEN BEGIN DEVICE,set_graphics_function=oldgraph END END handle_value,info.scaledv,scaledval,/set,/no_copy info.ext.replot = 0 info.ext.replot_cross = 0 ;; KEEP !X, !Y, !P !x = OLD_X !Y = OLD_Y !P = OLD_P END ; ; Setting the compound widget value ; PRO cw_pzoom_setv,ID,VALUE store = WIDGET_INFO(id,/child) WIDGET_CONTROL,store,get_uvalue=info,/no_copy IF datatype(value) NE 'STC' THEN BEGIN ;; ;; Setting the image ;; IF (SIZE(value))(0) NE 2 THEN $ MESSAGE,"CW_PZOOM value must be a two-dimensional array" ;; Remove old values handle_value,info.value,oldvalue,/no_copy handle_value,info.scaledv,oldscaled,/no_copy ;; Set the new value HANDLE_VALUE,info.value,value,/set ;; Update array size info.int.asize = (SIZE(value))(1:2) nold = N_ELEMENTS(oldvalue) IF nold EQ 4 OR nold EQ 0 THEN BEGIN ;; Previous image was the test value info.ext.xfocus = info.int.asize(0)/2 info.ext.yfocus = info.int.asize(1)/2 END info.ext.rescale = 1 END ELSE BEGIN ext = info.ext copy_tag_values,ext,value info.ext = ext END ;; Is a rescale in order? IF info.ext.rescale NE 0 THEN BEGIN cw_pzoom_scaleval,info info.ext.replot = 1 END ;; Is any replotting in order? IF info.ext.replot GT 0 OR info.ext.replot_cross GT 0 THEN $ cw_pzoom_plot,info ;; Store updated info structure WIDGET_CONTROL,store,set_uvalue=info,/no_copy END ; ; Returning some useful values. ; FUNCTION cw_pzoom_getv,ID store = WIDGET_INFO(id,/child) WIDGET_CONTROL,store,get_uvalue=info,/no_copy value = {CW_PZOOM_VALUEz,$ VALUE : info.value,$ CLIP : info.int.zero,$ XFOCUS : info.ext.xfocus,$ YFOCUS : info.ext.yfocus,$ ZOOM : info.ext.zoom,$ PLOTREG: info.int.preg} WIDGET_CONTROL,store,set_uvalue=info,/no_copy RETURN,value END ; ; EVENT handling. ; ; Most events will be draw events, but XTVSCALE events are also ; possible. ; FUNCTION cw_pzoom_event,ev ;; Default place to find the info structure ;; STORAGE = EV.ID WIDGET_CONTROL,STORAGE,get_uvalue=info,/no_copy ;; Assume a widget_draw event until proven otherwise ;; xtvscale = 0 auto = 0 ;; But it might be an XTVSCALE event IF datatype(info) EQ 'STR' THEN BEGIN ;; ;; Put back 'XTVSCALE' string ;; WIDGET_CONTROL,ev.id,set_uvalue=info ;; ;; Recheck it. ;; IF info EQ 'XTVSCALE' THEN BEGIN ;; ;; We need to know the info-keeper ;; STORAGE = WIDGET_INFO(ev.handler,/child) WIDGET_CONTROL,STORAGE,get_uvalue=info,/no_copy ;; ;; Ignore non-selected xtvscale events ;; IF ev.xtvscale_id NE info.ext.xtvscale THEN BEGIN PRINT,"Ignoring alien XTVSCALE event" GOTO,NO_EVENT END ;; ;; These are needed to generate the "set" structure. ;; xtvscale = 1 xpix = info.ext.xfocus ypix = info.ext.yfocus zoom = info.ext.zoom ;; Return event to main program GOTO,MAKE_EVENT END END ;; If we get here, it's a draw-window event. ;; IF info.int.cw_mouse NE -1 THEN BEGIN action = cw_tmouse(info.int.cw_mouse,ev) END ELSE $ CASE ev.press OF 1: action = 'ZOOM-' 2: action = 'ZOOMP' 4: action = 'ZOOM+' ELSE: action = 'IGNORE' END ;; These are default/old values ;; xpix = info.ext.xfocus ypix = info.ext.yfocus zoom = info.ext.zoom ;; No event unless proven otherwise ;; evnt = 0 ;; No autonomous mode until proven otherwise ;; auto = 0 ;; ;; If the action is on the ignore_action list we should (guess what) ;; ignore it. ;; IF STRPOS(info.ext.ignore_action,'('+action+')') GT -1 THEN GOTO,NO_EVENT CASE action OF 'ZOOM-': BEGIN IF zoom EQ info.ext.minzoom THEN GOTO,NO_EVENT zoom = (zoom/2) > info.ext.minzoom IF zoom GE 1 THEN zoom = round(zoom) END 'ZOOM+': BEGIN maxzoom = info.ext.maxzoom IF maxzoom EQ 0 THEN BEGIN npixels_displayed = [info.int.zero(2)-info.int.zero(0)+1,$ info.int.zero(3)-info.int.zero(1)] IF min(npixels_displayed) LT 3 THEN maxzoom = info.ext.zoom $ ELSE maxzoom = 2*info.ext.zoom END IF zoom EQ maxzoom THEN GOTO,NO_EVENT zoom = (zoom*2) < maxzoom IF zoom GE 1 THEN zoom = round(zoom) END ELSE: BEGIN ;; *NOT* a zoom in/out event, we should report new coordinates ;; Find the plot region preg = pfind(ev,found) IF NOT found THEN BEGIN ;; Outsize clip region, incremental positioning. prestore,info.int.preg xpix = info.ext.xfocus ypix = info.ext.yfocus IF ev.x LT !P.clip(0) THEN xpix = xpix-1 IF ev.x GT !P.clip(2) THEN xpix = xpix+1 IF ev.y LT !P.clip(1) THEN ypix = ypix-1 IF ev.y GT !P.clip(3) THEN ypix = ypix+1 END ELSE BEGIN ;; Inside clip region, convert to pixels xpix = FIX(pconvert(preg,ev.x,/dev,/to_pix)) ypix = FIX(pconvert(preg,ev.y,/dev,/to_pix,/Y)) xpix = xpix+info.int.zero(0) ypix = ypix+info.int.zero(1) END END ENDCASE MAKE_EVENT: preg = info.int.preg set = {CW_PZOOM_SET,$ XFOCUS : xpix,$ YFOCUS : ypix,$ ZOOM : FLOAT(zoom),$ REPLOT : 1,$ RESCALE : (XTVSCALE NE 0)} old = {CW_PZOOM_SET,$ XFOCUS : info.ext.xfocus,$ YFOCUS : info.ext.yfocus,$ ZOOM : info.ext.zoom,$ REPLOT : 0, $ RESCALE : 0B} IF xtvscale THEN $ evnt = {CW_PZOOM_XTVSCALE_EVENT,$ ID:ev.handler,$ TOP:ev.top,$ HANDLER:0L,$ XTVSCALE : XTVSCALE,$ ; Signal a rescaling event SET : set,$ OLD : old,$ PLOTREG:PREG,$ EVENT:EV} $ ELSE $ evnt = {CW_PZOOM_EVENT,$ ID:ev.handler,$ TOP:ev.top,$ HANDLER:0L,$ XTVSCALE : XTVSCALE,$ ; Signal a rescaling event SET : set,$ OLD : old,$ PLOTREG:PREG,$ EVENT:EV} ;; Keeping it all inside the array evnt.set.xfocus = (evnt.set.xfocus > 0) < (info.int.asize(0)-1) evnt.set.yfocus = (evnt.set.yfocus > 0) < (info.int.asize(1)-1) ;; Autonomous mode? auto = info.int.auto NO_EVENT: ;; NO replotting so far, just store info structure WIDGET_CONTROL,STORAGE,set_uvalue=info,/no_copy ;; Autonomous mode means always acknowledge any event and ;; gobble it up. ;; IF auto AND datatype(evnt) EQ 'STC' THEN BEGIN WIDGET_CONTROL,ev.handler,set_value=evnt.set RETURN,0 END RETURN,evnt END ;; This routine is called once, upon realization of the widget ;; Display the value. Make one up if it's not set. ;; PRO cw_pzoom_realize,ID STORAGE = WIDGET_INFO(ID,/CHILD) WIDGET_CONTROL,STORAGE,get_uvalue = info,/no_copy ;; Get the tv window id. ;; WIDGET_CONTROL,info.int.draw,get_value = tv info.int.tv = tv ;; Check the value ;; handle_value,info.value,value,/no_copy ;; Make dummy value if none set. IF N_ELEMENTS(value) EQ 0 OR (SIZE(VALUE))(0) NE 2 OR $ datatype(value) EQ 'COM' OR datatype(value) EQ 'STC' THEN BEGIN value = [ [1,0],$ [0,1]] END ;; Store array size ;; info.int.asize = (SIZE(value))(1:2) ;; Initial position ;; info.ext.xfocus = info.int.asize(0)/2 info.ext.yfocus = info.int.asize(1)/2 ;; Store value and info structure ;; HANDLE_VALUE,info.value,value,/set,/no_copy WIDGET_CONTROL,storage,set_uvalue=info,/no_copy ;; Force a redisplay ;; WIDGET_CONTROL,id,set_value = {rescale:1} END ;; 48 keywords ... ! FUNCTION cw_pzoom,on_base, $ value=value, $ xwsize=xwsize, ywsize=ywsize,$ xdsize=xdsize, ydsize=ydsize, origo=origo,$ uvalue=uvalue, no_copy=no_copy, $ cw_mouse=cw_mouse,motion_events=motion_events,$ $ $;; PLOT keywords: $ background=background, charsize=charsize, $ xcharsize=xcharsize, ycharsize=ycharsize, $ charthick=charthick, color=color, font=font, $ xgridstyle=xgridstyle, ygridstyle=ygridstyle, $ xstyle=xstyle, ystyle=ystyle, subtitle=subtitle, $ xthick=xthick,ythick=ythick, $ xtickformat=xtickformat, ytickformat=ytickformat, $ xticks=xticks, yticks=yticks, ticklen=ticklen, $ xticklen=xticklen, yticklen=yticklen, title=title, $ xtitle=xtitle, ytitle=ytitle, $ $ $;; Data coordinate system: $ origin=origin, scale=scale, $ squarepix=squarepix,stretch=stretch, $ $ $;; Color scaling $ xtvscale=xtvscale,missing=missing, $ color_missing=color_missing,$ $ $;; Limitations $ maxzoom=maxzoom,minzoom=minzoom,$ $ $;; Crosshair $ cross=cross,cross_color=cross_color, $ cross_graph=cross_graph,$ $ $;; Miscellaneous $ autonomous=autonomous, $ ignore_action=ignore_action ;+ ; ; KEYWORD DEFAULTS/EXPLANATIONS ; ; All keywords not marked with * may be altered after widget creation ; through the ; WIDGET_CONTROL,CW_ID,SET_VALUE={:} ; mechanism. ; Widgety things default,xwsize,200 ;* Widget_draw xsize default,ywsize,200 ;* Widget_draw ysize default,xdsize,140 ;* Display area xsize default,ydsize,140 ;* Display area ysize default,origo,[50,40] ; Origin of display area (pixels) default,uvalue,'CW_PZOOM' ;* default,no_copy,0 ;* For setting uvalue of this Compound widget default,CW_MOUSE,-1L ;* Compound widget mouse control box. default,motion_events,0 ;* Make the WIDGET_DRAW return motion events ; ; Standard PLOT keywords ; blank = STRING(255b) default,background,-1L ; Use !P.background default,charsize,-1.0 ; Use !P.charsize default,xcharsize,-1.0 ; Use !X.charsize default,ycharsize,-1.0 ; Use !Y.charsize default,charthick,-1.0 ; Use !P.charthick default,color,-1L ; Use !P.color default,font,-2L ; Use !P.font default,xgridstyle,-1L ; Use !X.gridstyle default,ygridstyle,-1L ; Use !Y.gridstyle default,xstyle,-1L ; Use !X.style default,ystyle,-1L ; Use !Y.style default,subtitle,blank ; Use !P.subtitle default,xthick,-1.0 ; Use !X.thick default,ythick,-1.0 ; Use !Y.thick default,xtickformat,blank ; Use !X.tickformat default,ytickformat,blank ; Use !Y.tickformat default,xticks,0 ; Use !X.ticks default,yticks,0 ; Use !Y.ticks default,ticklen,-0.05 ; 0.0 Means use !P.ticklen default,xticklen,0.0 ; Use !X.ticklen default,yticklen,0.0 ; Use !Y.ticklen default,title,blank ; Use !P.title default,xtitle,blank ; Use !X.title default,ytitle,blank ; Use !Y.title ; Data coordinate system default,origin,[0.0,0.0] ; x,y coordinates of pixel [0,0] default,scale,[1.0,1.0] ; x,y size of pixels, physical units default,squarepix,0 ; Ignore phys. pixel size (treat them square) default,stretch,0 ; Ign. phys. pix. size, stretch to fit display ; Color scaling default,xtvscale,-1L ; Color scaling. default,missing,-1.0D ; Missing value. default,color_missing,0B ; Color used for missing valued pixels ; Limitations default,maxzoom,0 ; Max. zoom, 0 means the sensible limit. default,minzoom,0.5 ; Min. zoom -how much overwiew could you want! ; Crosshair default,cross,30 ; Crosshair size, pixels default,cross_color,!P.color ; Crosshair color default,cross_graph,3 ; Crosshair graphics function ; Miscellaneous default,autonomous,0 ;* Auto-redisplay. ; Ignore these ACTIONs: default,ignore_action,'(IGNORE)(MOTION)(RELEASE1)(RELEASE2)(RELEASE3)' ;- ;; ;; Once more: Check variable types ;; keyword = 0 scalar = 0 real = typ(/rea) natural = typ(/nat) strng = typ(/str) lng = typ(/lon) ; Widgety things parcheck,xwsize, keyword, real, scalar,'XWSIZE' parcheck,ywsize, keyword, real, scalar,'YWSIZE' parcheck,xdsize, keyword, real, scalar,'XDSIZE' parcheck,ydsize, keyword, real, scalar,'YDSIZE' parcheck,origo, keyword, natural, 1,'ORIGO' no_copy = KEYWORD_SET(no_copy) parcheck,cw_mouse, keyword, lng, scalar,'CW_MOUSE' motion_events = KEYWORD_SET(motion_events) ; PLOT keywords: parcheck,background, keyword, natural,scalar,'BACKGROUND' parcheck,charsize, keyword, real, scalar,'CHARSIZE' parcheck,xcharsize, keyword, real, scalar,'XCHARSIZE' parcheck,ycharsize, keyword, real, scalar,'YCHARSIZE' parcheck,charthick, keyword, real, scalar,'CHARTHICK' parcheck,color, keyword, natural,scalar,'COLOR' parcheck,font, keyword, natural,scalar,'FONT' parcheck,xgridstyle, keyword, natural,scalar,'XGRIDSTYLE' parcheck,ygridstyle, keyword, natural,scalar,'YGRIDSTYLE' parcheck,xstyle, keyword, natural,scalar,'XSTYLE' parcheck,ystyle, keyword, natural,scalar,'YSTYLE' parcheck,subtitle, keyword, strng, scalar,'SUBTITLE' parcheck,xthick, keyword, real, scalar,'XTHICK' parcheck,ythick, keyword, real, scalar,'YTHICK' parcheck,xtickformat, keyword, strng, scalar,'XTICKFORMAT' parcheck,ytickformat, keyword, strng, scalar,'YTICKFORMAT' parcheck,xticks, keyword, natural,scalar,'XTICKS' parcheck,yticks, keyword, natural,scalar,'YTICKS' parcheck,ticklen, keyword, real, scalar,'TICKLEN' parcheck,xticklen, keyword, real, scalar,'XTICKLEN' parcheck,yticklen, keyword, real, scalar,'YTICKLEN' parcheck,title, keyword, strng, scalar,'TITLE' parcheck,xtitle, keyword, strng, scalar,'XTITLE' parcheck,ytitle, keyword, strng, scalar,'YTITLE' ; Data coordinate system parcheck,origin,keyword,real,1,'ORIGIN' parcheck,scale,keyword,real,1,'SCALE' squarepix = KEYWORD_SET(squarepix) stretch = KEYWORD_SET(stretch) ; Color scaling: parcheck,xtvscale, keyword,lng, [0,1],'XTVSCALE' parcheck,missing, keyword,real, scalar,'MISSING' parcheck,color_missing,keyword,natural,scalar,'COLOR_MISSING' ; Limitations parcheck,maxzoom, keyword,real, scalar,'MAXZOOM' parcheck,minzoom, keyword,real, scalar,'MINZOOM' ; Crosshair parcheck,cross, keyword,natural,scalar,'CROSS' parcheck,cross_color, keyword,natural,scalar,'CROSS_COLOR' parcheck,cross_graph, keyword,natural,scalar,'CROSS_GRAPH' ; Miscellaneous autonomous = KEYWORD_SET(autonomous) ; Ignore actions parcheck,ignore_action,keyword,strng, scalar,'IGNORE_ACTION' ; N_ELEMENTS checks on origin, scale, and origo IF N_ELEMENTS(origin) NE 2 THEN $ MESSAGE,"Keyword ORIGIN in CW_PZOOM must have two elements" IF N_ELEMENTS(scale) NE 2 THEN $ MESSAGE,"Keyword SCALE in CW_PZOOM must have two elements" IF N_ELEMENTS(origo) NE 2 THEN $ MESSAGE,"Keyword ORIGO in CW_PZOOM must have two elements" ; ; That's all! ; wsize = LONG([xwsize,ywsize]) dsize = LONG([xdsize,ydsize]) base = WIDGET_BASE(on_base, space=0, xpad=0, ypad=0,$ uvalue=uvalue,no_copy=no_copy, $ event_func='cw_pzoom_event', $ func_get_value='cw_pzoom_getv', $ pro_set_value='cw_pzoom_setv', $ notify_realize='cw_pzoom_realize') draw = WIDGET_DRAW(base, $ xsize=wsize(0),ysize=wsize(1), $ /button_events,motion_events=motion_events) ;; Used for signalling from XTVSCALE signal = WIDGET_BASE(base,uvalue='XTVSCALE',map = 0) ;; Hook up for events from XTVSCALE FOR i=0,N_ELEMENTS(XTVSCALE)-1 DO BEGIN IF (XTVSCALE)(i) GE 0L THEN dummy = xtvscale((xtvscale)(i),signal=signal) END ;; External values ;; These can be set by the user "on the fly" ext = $ { $ ;; cw_pzoom_disp,$ origo : origo, $ ; Origin of viewing area $ $;; PLOT keywords: $ background : LONG(BACKGROUND), $ charsize : FLOAT(CHARSIZE), $ xcharsize : FLOAT(XCHARSIZE), $ ycharsize : FLOAT(YCHARSIZE), $ charthick : FLOAT(CHARTHICK), $ color : LONG(COLOR), $ font : LONG(FONT), $ xgridstyle : LONG(XGRIDSTYLE), $ ygridstyle : LONG(YGRIDSTYLE), $ xstyle : LONG(XSTYLE), $ ystyle : LONG(YSTYLE), $ subtitle : SUBTITLE, $ xthick : FLOAT(XTHICK), $ ythick : FLOAT(YTHICK), $ xtickformat : XTICKFORMAT, $ ytickformat : YTICKFORMAT, $ xticks : LONG(XTICKS), $ yticks : LONG(YTICKS), $ ticklen : FLOAT(TICKLEN), $ xticklen : FLOAT(XTICKLEN), $ yticklen : FLOAT(YTICKLEN), $ title : TITLE, $ xtitle : XTITLE, $ ytitle : YTITLE, $ $ $;; Data coordinate system $ origin : origin,$ scale : scale,$ squarepix : squarepix,$ stretch : stretch,$ $ $;; Color scaling $ missing : DOUBLE(missing),$ xtvscale : (xtvscale)(0),$ ; Xtvscale_id color_missing : color_missing,$ $ $;; Limitations $ maxzoom : FLOAT(maxzoom),$ minzoom : FLOAT(minzoom),$ $ $;; Crosshair $ cross : FLOAT(cross),$ cross_color : cross_color,$ cross_graph : cross_graph,$ $ $;; Miscellaneous $ ignore_action : ignore_action,$ $ $;; State/action variables $ XFOCUS : 0,$ ; Cannot be set by keyword values. YFOCUS : 0,$ ; zoom : 1.0,$ ; REPLOT : 0,$ ; REPLOT_CROSS : 0,$ ; RESCALE : 0 $ ; } ;; Internal values ;; These are fixed for the lifetime of the compound widget. int = $ { $ ;; cw_pzoom_int,$ simple : 1,$ ZERO : [0L,0,0,0],$ ; Data Pixel number of *displayed* pixel (0,0) ASIZE : [0,0],$ ; Data array size WSIZE : wsize,$ ; Window size (total) DSIZE : dsize,$ ; Display size (size of viewing area). auto : autonomous,$ ; Block events? DRAW : draw,$ preg : 0L,$ CW_MOUSE : CW_MOUSE,$ tv : -1L} kill_handles = [handle_create(),handle_create(),handle_create()] ;; Handles will be automatically killed when the draw widget is destroyed. handle_killer_hookup,kill_handles,group_leader=draw info = $ { $ ;;cw_pzoom_info,$ value : kill_handles(0),$ scaledv : kill_handles(1),$ im_h : kill_handles(2),$ kill_handles:kill_handles,$ ext : ext,$ int: int} IF N_ELEMENTS(value) GT 0 THEN handle_value,info.value,value,/set WIDGET_CONTROL,draw,set_uvalue=info,/no_copy RETURN,base END