;+ ; NAME: ; DISPLAY ; ; VERSION: ; 3.0. Changes: added GRIDSTYLE keyword, NO_GUI keyword, MAKE_PNG keyword, MARGIN_LEFT and MARGIN_BOTTOM keywords. ; 2.2. Changes: added ALL keyword for more convenient ROI linking and coloring. Added ability to use MPFIT ; directly in DISPLAY. Download the complete MPFIT library from http://cow.physics.wisc.edu/~craigm/idl/fitting.html ; 2.1. Changes: functionality corrected for use as a child in widget applications, such as removal of all plots ; in the same Display window if OREF is provided and is a valid object reference. ; 2.0 Changes: multiple modifications to 1.0 and 1.1, number of lines of code reduced by 20%. ; ; PURPOSE: ; The purpose of this routine is to provide transparent replacement for PLOT ; and OPLOT routines. Operates in 2D space. ; ; AUTHOR: ; Pavel A. Romashkin, Ph. D. ; ; CATEGORY: ; Graphics. ; ; CALLING SEQUENCE: ; DISPLAY, [X], [Y], [/OVER], [OPTIONAL_KEYWORDS] ; ; INPUTS: ; Optional X and/or Y, vectors to be displayed. ; ; KEYWORD PARAMETERS: ; Accepts all IDLgrPlot keywords at the time of creation of the plot or with NAME and OVER keywords set. ; Accepts all IDLgrText and IDLgrLegend keywords. Use with /OVER and /LEGEND, or when setting TEXT. ; Accepts standard PLOT keywords (XRANGE, YRANGE, PSYM etc.) but is not guaranteed to honor them all. ; ALL: Apply a command to all open DISPLAY windows. Useful for actions such as ROI_COLOR, LINK=x etc. Do not ; supply any X-Y arguments, they will be ignored at best. ; BACKGROUND: set to three-point vector, RGB, in the form [255, 255, 255]. Defaults to white. ; COLOR: set to three-point vector, RGB, in the form [0, 255, 0]. Defaults to black. Will act on the item currently ; being added, or on the named item specified along with COLOR. ; CONTOUR: displays a contour plot of data. If set, provide data in the form of X, Y, Z, where Z must be a 2D array ; of elevation values, X and Y may be 1 or 2D arrays of axis coordinates for Z. ; CTABLE: color table to use to color CONTOUR levels. Defaults to 33 (Blue-Red). ; FILENAME: used to provide destination file name for SAVE and other file output functions. ; FONT: set to font name, in the form 'Helvetica' (default). ; GRIDSTYLE: set to a number from 0 to 6 to make major tick grid, or to negative number for major and minor tick grid. ; 0 = Solid line, 1 = dotted, 2 = dashed, 3 = dash dot, 4 = dash dot dot, 5 = long dash, 6 = no line drawn ; GROUP_LEADER: set ta valid widget ID to use it as a geroup leader for Display. ; HIDE: use to hide or display a named plot or other named item. ; KEEP_SCALE: set when adding a plot to prevent autoscaling. To rescale to full range later, use double-click. ; KILL: use with NAME to delete a named element of DISPLAY programmatically. Right_double_click will delete, too. ; LEGEND: will produce a self-updating legend. Use with /OVER. Legend's position is static with respect to plots. ; /LEGEND, LOCATION = [x, y] use with LEGEND to specify legend position in normalized coordinates. ; /LEGEND, LOCATION = [x, y], /DATA indicates that the legend will be positioned in data coordinates. ; LEGEND_TITLE: set to a valid string containing a title for the legend. Use with /OVER. ; LINESTYLE: 0 through 5 as for PLOT, additionally Linestyle=6 for No line. ; LINK: set to an integer value. This value is used to identify linked windows. If two or more DISPLAY ; windows have the same LINK value and contain plots with the same NAMEs, a ROI drawn to one window ; will cause ROI selection in other linked windows. ROI is drawn using the middle mouse button. ; ROI plots can be removed from DISPLAYs by right-double-clicking on them. ; LINK (< 0) : if LINK is set to a negative number, it allows additional features. See WIPE keyword. ; MAKE_PNG: use with FILENAME keyword to specify the file to write output from Display into. If FILENAME not specified, ; you will be prompted for the file name. ; MARGIN_LEFT: set to a value like 0.2 to increase the margin if it is insufficiently wide to accommodate the axis title. ; MARGIN_BOTTOM: set to a value like 0.2 to increase the margin if it is insufficiently tall to accommodate the axis title. ; MP_FIT: use to add or test a functional fit to data deisplayed in a Display window. Syntax as follows: ; Display, /over, name='DataToFit', fit_name='The_Fit', fit_func='my_linear', coefs_in=[2.0D, 0.5D], $ ; coefs_out=resulting_fit_coefs ; To use, MPFIT must be present (http://cow.physics.wisc.edu/~craigm/idl/fitting.html). Fitting functions must ; be in form: ; ********************* ; ; Linear function to use for fitting with MPFIT. P is a 2-element vector. Use both to fit and plot the fit. ; function fit_line, p, x=x, y=y ; if n_elements(y) eq 0 then return, p[0] * X + P[1] ; p = double(p) ; return, y - (p[0] * X + P[1]) ; end ; ********************* ; NLEVELS: number of levels to be displayed in CONTOUR. Defaults to 30. ; NAME: optional name for a plot being added or text placed in the window. Later, a modifying command with ; OVER keyword set can be used to modify that plot or text appearance, or hide it. ; NO_GUI: set this keyword to create a hidden DISPLAY with no window on the screen. Useful for making picture files without ; seeing the Display window first. Use with OREF keyword to point to the target DISPLAY instance. ; OREF: returns the object reference to the Display window. Use OREF when accessing a Display window ; created as a child in a widget hierarchy. ; OUT: zoom a DISPLAY to autoscale, or use with XRANGE or YRANGE autoscale along an axis. ; OVER: set to apply changes to an existing topmost Display window. ; PARENT: set to valid Widget_base ID. Will cause Display to appear as a child of that base. Use with OREF keyword. ; PRINTOUT: set to print the newly created Display window. Use with OVER to print existing topmost window. ; POLYGON: creates polygon of specified COLOR using X and Y as vertex coordinates. ; PSYM: see Graphic Keywords for details. Same as in PLOT procedure. ; RESTORE_FILE: set to a string containing the name of a file where a DISPLAY was saved, to restore that DISPLAY. ; SAVE: set this keyword to save contents of DISPLAY to a file. Use with optional FILENAME keyword. ; ROI_COLOR: set to three-point vector, RGB, to specify a ROI color. Defaults to red. ; TEXT: set to a valid string or string array. Most formatting keywords for the text (object) will be also honored. ; STATIC: use to place text "on glass" so it does not scale when zooming or adding plots. ; DATA or NORMALIZED: choose the coordinate system for the text. ; LOCATION: use with TEXT to specify position in the chosen coordinates. ; NAME: set to a string to identify a particular text block. Then, this text can be altered using the given name. ; INTERVAL: set to specify line spacing for multi-line text, in fraction of character height as float point value. ; VERT_COLOR: color matrix bytarr(3, n_elements(x)) describing RGB colors of each vertex of the plot. ; Symbols will be of the color set by COLOR keyword. ; TITLE, XTITLE, YTITLE, WTITLE: set to a string containing (short) text for plot, axes or window title. ; WIPE: set this keyword together with the NAME of a plot that is a LINKed plot to delete all corresponding points ; from the original plot. For example, if you DISPLAYed data named "My_Plot", and want to delete some points to ; fit the rest of the data. Do this: ; DISPLAY, x, y, name='My_Plot', LINK=-1, /Legend ; Using middle mouse button, draw a region surrounding the points you want to delete, then type ; DISPLAY, /OVER, /WIPE, Name='Linked My_Plot' ; ; OBJECT METHODS: ; Transparent to the end user. See individual routines for functionality description. ; GET_DATA: invoke this function method on an instance of PARgrDisplay returned by OREF keyword. The result ; is a structure which fields 1 through X are FLTARR(3, *) of plots data from the object. ; ; COMMON BLOCKS: ; PARgrDisplay_top_window. One common variable, type - object reference (or object array). ; Used to keep track of the topmost Display window so that a user can create and ; modify several of them, and for maintaining linked windows in the linked ROI mode. ; ; SIDE EFFECTS: ; None. ; ; RESTRICTIONS: ; Designed under IDL 5.4 and 5.5. Version-specific features include: ; COMPILE_OPT calls. In earlier versions of IDL, comment these lines out. ; Some keyword parameters are passed undefined. IDL 5.3 and above allow that. ; Earlier versions of IDL may require checking for validity of variables. ; ; EXAMPLE: ; See http://spot.colorado.edu/~romashki/idl/display.html for details on the use of the program. ; Creating a new Display window (plots can be appended): ; DISPLAY ; ; Creating a new Display with existing data: ; DISPLAY, sin(findgen(100)*0.1)+1.0, psym=4, linestyle=6, color=[255,0,0], $ ; xtitle='My X axis', ytitle='My Y axis', title='My nice plots', $ ; name='plot_1', wtitle='Top window' ; ; Appending a plot to the existing topmost Display window and making the window linked using the ID of 1: ; DISPLAY, sin(findgen(100)*0.15)+0.9, psym=5, color=[0,0,200], LINK=1, /OVER ; ; Changing properties of the existing topmost Display window: ; DISPLAY, background=[200,200,0], title='New plot title', name='plot_1', $ ; color=[200,0,0], /OVER ; ; PROCEDURE: ; Display will create and maintain automatic ranges for the window. When a plot ; is added that expands the range, or a plot is removed that had the largest range, ; the window can re-scale to accomodate the largest present plot. Use /KEEP_SCALE ; to prevent autoscaling. ; Legend and / or Text items can be freely moved using dragging with the right mouse button, and ; can be deleted using right-double-click. ; Zooming is performed by Left-mouse-drag in the window. Full zoom-out is performed ; using Left-double-click in the window. ; Any item, including plots and axes, can be removed from Display by Right-double-clicking on that plot. ; No "undo" functionality is provided. ; Multiple windows are allowed to be active at the same time. Another plot can be ; added to any window by bringing it to the foreground (clicking on it with the mouse) ; and executing Display, . . . , /OVER command. ; Co-existing windows can interact with each other through the use of LINK keyword parameter. ; Windows with the same, non-zero LINK value and containing the same plot NAMEs, will produce ; a region of interest (ROI) highlight if a ROI is drawn in one of such linked windows. Use middle ; mouse button to draw a ROI. ; LINK can be set to another value at any time using /OVER keyword. ; Formatting commands can be issued to an existing Display window without adding ; new plots by using Display, . . . , /OVER command. ; Programmatically, can be used as a child to any base widget in any widget program ; (refer to PARENT, OREF and GROUP_LEADER keywords). ; ; EXPANDABILITY: ; Right, middle and left mouse buttons can be used while Single, Double clicking or ; dragging. Different color ROI selection rectangles are provided to indicate action. ; Supplying user procedures allows to perform various actions on the data in ROI. ; ; MODIFICATION HISTORY: ; Written: P.A.Romashkin, 08-2000 ; Documented, P.A.Romashkin, 8-2000 ; GET_DATA, SAVE and RESTORE added, PAR, 12-2000 ; Documented additions, P.A.Romashkin, 12-2000 ; Separated mouse events into object methods and added TEXT, PAR, 8-2001 ; Added Polygon ability, PAR, 9-2001 ; Added Contour ability, fixed some bugs, documented additions, PAR, 11-2001 ; Added/documented data/normalized space for Text labels, On Glass feature. Fixed Windows ; platform "First Display window gets no focus" bug, PAR, 2-2002 ; Fixed XY-RANGE, added zooming and autoscaling from command line - 11-2002, PAR ;- ; ************************************************************************* ; Provides event handling for the top level widget and draw window. pro Display_event, event compile_opt IDL2, obsolete, hidden Common PARgrDisplay_top_window, got_focus ; Provide error handling. Catch, Err_code if (Err_code ne 0) then begin print,'PARgrDisplay_event error caught:' help, /last_message catch, /cancel return endif ; Get object reference out, as it is used virually everwhere. widget_control, event.id, get_uval=Object ; ---------------------------- THESE ARE TOP BASE EVENTS ---------------------------- ; Now, handle the possibility of focusing event. User will be able to Oplot to existing window. ; Common block is used to store the information about topmost window as it gains focus. if tag_names(event, /STRUCTURE_NAME) EQ 'WIDGET_KBRD_FOCUS' then begin if (event.enter eq 1) then begin ; This means Display window was brought to the top. if n_elements(got_focus) eq 0 then begin ; If this is the first object ever, reset GOT_FOCUS. got_focus = object return endif if not obj_valid(got_focus[0]) then begin ; If the first object is invalid, replace it. got_focus[0] = object got_focus = got_focus[where(obj_valid(got_focus))] return endif if got_focus[0] eq object then return ; Don't waste time if it was on top already. index = where(got_focus eq object, count) ; Locate the object that was brought up in the array... if count gt 0 then got_focus[index] = obj_new() ; and invalidate the existing reference to it. got_focus = got_focus[where(obj_valid(got_focus))] ; Now, got_focus does NOT contain the Object reference. Add it: if got_focus[0] -> is_linked() eq 0 then got_focus[0] = object else got_focus = [object, got_focus] ; Now, common block has top Display at 0 position, and Linked Displays in other positions of got_focus. endif return endif ; Lastly, it may be resize event. Change the size of daughter Draw widget. if tag_names(event, /STRUCTURE_NAME) EQ 'WIDGET_BASE' then begin widget_control, widget_info(event.id, /child), draw_xsize=event.x, draw_ysize=event.y Object -> draw return endif ; ---------------------------- END OF TOP BASE EVENTS ---------------------------- ; Process mouse events. Use all options of Single or Double click, drag and release with three mouse buttons. case event.type of 0: object -> mouse_press, event ; Mouse press 1: object -> mouse_release, event ; Mouse release 2: object -> mouse_drag, event ; Mouse drag endcase end ;************************************************************************* ; Method called on mouse press in the DISPLAY window. Helps structure event handling procedure. pro PARgrDisplay::mouse_press, event compile_opt IDL2, obsolete, hidden if n_elements(*self.plot_list) eq 0 then return if obj_valid((*self.plot_list)[0]) eq 0 then return temp = self.obj_win -> pickData(self.view, (*self.plot_list)[0], [event.x, event.y], coords) if event.clicks eq 1 then begin ; Process single-click events. self.mouse_press = [event.x, event.y] self.mouse_press_data = coords self.mouse_button=event.press case event.press of 1: begin ; Left button, Zoom. x = event.x y = event.y widget_control, event.id, /draw_motion ; Turn on motion events. self.roi_object = obj_new('IDLgrPolygon', color=[0, 200, 0], linestyle=0, style=1, [x, x, x, x], [y, y, y, y], $ xcoord_conv=self.norm_x, ycoord_conv=self.norm_y) ; Use data coordinate system. self.model -> add, self.roi_object end 2: begin ; Middle button - create ROI object. widget_control, event.id, /draw_motion ; Turn on motion events. ; If self is not linked, allow drawing of ROI but make it dotted. if self.link eq 0 then begin linestyle=1 ; loc = [mean(self.crange[*, 0]), mean(self.crange[*, 1])] loc = [self.x_crange[0] + (self.x_crange[1] - self.x_crange[0])/2, $ self.y_crange[0] + (self.y_crange[1] - self.y_crange[0])/2] temp = obj_new('IDLgrText', 'This window is not linked with others.', location=loc, /recompute, $ color=[255,0,0], alignment=0.5, xcoord_conv=self.norm_x, ycoord_conv=self.norm_y) self.model -> add, temp self.trash -> add, temp endif else linestyle=0 self.roi_object = obj_new('IDLgrRoi', coords[0], coords[1], xcoord_conv=self.norm_x, ycoord_conv=self.norm_y, $ linestyle=linestyle, color=self.roi_color) self.model -> add, self.roi_object self.obj_win -> draw, self.view end 4: begin ; Right mouse click and drag is used to move objects on Display. ; Drag events are not needed for moving objects, just as nothing else is needed. ; All action on moving objects is performed on mouse release. ; widget_displaycontextmenu, event.id, event.x, event.y, draw_context end else: endcase endif else begin ; Process double-click events. case event.press of 1: begin ; Left button double-click zooms fully out. self -> zoom, /out, /draw end 2: begin ; Middle-double-click is for user added functionality. ; The following prints the coordinates of the double click. ;temp = self.obj_win -> pickData(self.view, self.plot_list[0], [event.x, event.y], coords) ;print, 'X coord.: ', coords[0], ' Y coord.: ', coords[1] ; The following is optional. It prints out plot name, if double click hit a plot. temp = self.obj_win -> select(self.view, [event.x, event.y]) if obj_valid(temp[0]) then begin temp[0] -> getProperty, name=name print, name endif end 4: begin ; Right-double-click kills selected plot. plot_id = (self.obj_win -> select(self.view, self.mouse_press))[0] if not obj_valid(plot_id) then return obj_destroy, plot_id *self.plot_list = (*self.plot_list)[where(obj_valid(*self.plot_list)) > 0] self -> legend, /update self -> rescale, /keep_scale self.obj_win -> draw, self.view end else: endcase endelse end ;************************************************************************* ; Method called on mouse release in the DISPLAY window. Helps structure event handling procedure. pro PARgrDisplay::mouse_release, event compile_opt IDL2, obsolete, hidden widget_control, event.id, draw_motion=0 ; Turn motion events off. if n_elements(*self.plot_list) eq 0 then return if obj_valid((*self.plot_list)[0]) eq 0 then return case event.release of 1: begin ; This release event is left mouse button and will zoom in. obj_destroy, self.roi_object ; Kill ROI object. Must be killed BEFORE zooming to avoid POLYGON mixup. if min(abs(self.mouse_press - [event.x, event.y])) le 5 then return temp = self.obj_win -> pickData(self.view, (*self.plot_list)[0], [event.x, event.y], coords) self -> zoom, xrange=[self.mouse_press_data[0], coords[0]], yrange=[self.mouse_press_data[1], coords[1]] self.obj_win -> draw, self.view end 2: begin ; This event is middle mouse button and will select ROI. if not obj_valid(self.roi_object) then return if min(abs(self.mouse_press - [event.x, event.y])) gt 5 then $ if self.link ne 0 then self -> link_roi, self.roi_object obj_destroy, self.roi_object trash = self.trash -> get(/all, count=count) ; Get all trashed references out for destruction self.trash -> remove, /all ; Remove all trashed object references from self.Trash if count gt 0 then obj_destroy, trash ; Kill all trashed objects self.obj_win -> draw, self.view end 4: begin ; If did not move cursor far enough, don't move items. if min(abs(self.mouse_press - [event.x, event.y])) le 5 then return selected_id = (self.obj_win -> select(self.view, self.mouse_press))[0] if not obj_valid(selected_id) then return temp = self.obj_win -> pickData(self.view, (*self.plot_list)[0], [event.x, event.y], coords) x0 = self.mouse_press_data[0] y0 = self.mouse_press_data[1] x1 = coords[0] y1 = coords[1] ; If a legend was selected, use its Translate method in normalized coordinates. if obj_class(selected_id) eq 'IDLGRLEGEND' then begin delta_x = (x1 - x0)/(self.x_crange[1] - self.x_crange[0]) delta_y = (y1 - y0)/(self.y_crange[1] - self.y_crange[0]) selected_id -> translate, delta_x, delta_y, 0 endif else $ ; If it was a text object, use its Location property. if obj_class(selected_id) eq 'IDLGRTEXT' then begin selected_id -> getProperty, location=loc, strings=strings, uvalue=data_coord ; Text might have been placed in normalized coordinates. If so, keep them. if data_coord eq 'NORMALIZED' then begin x1 = x1 / (self.x_crange[1] - self.x_crange[0]) ; Normalize X y1 = y1 / (self.y_crange[1] - self.y_crange[0]) ; Normalize Y endif ; If in DATA coords, it will behave correctly without changing x1 and y1. n_lines = n_elements(strings) ; Keep row spacing for multiple rows. if n_lines eq 1 then loc = [x1, y1] else begin offset = (loc[1, 0:1]-shift(loc[1, 0:1],1))[1] ; Obtain row spacing... loc[0, *] = x1 loc[1, *] = indgen(n_lines)*offset + y1 ; ...and apply this same spacing at new location. endelse selected_id -> setProperty, location=loc endif self.obj_win -> draw, self.view end else: endcase end ;************************************************************************* ; Method called on mouse drag in the DISPLAY window. Helps structure event handling procedure. pro PARgrDisplay::mouse_drag, event compile_opt IDL2, obsolete, hidden temp = self.obj_win -> pickData(self.view, (*self.plot_list)[0], [event.x, event.y], coords) x0 = self.mouse_press_data[0] y0 = self.mouse_press_data[1] x1 = coords[0] & y1 = coords[1] case self.mouse_button of 1: begin ; Update the zooming rectangle. if not obj_valid(self.roi_object) then return ; On some fast drags, a ROI does not get established. self.roi_object -> setProperty, data=[[x0, y0], [x0, y1], [x1, y1], [x1, y0], [x0, y0]] self.obj_win -> draw, self.view end 2: begin ; Update ROI object. self.roi_object -> appendData, x1, y1 self.obj_win -> draw, self.view end 4: begin end else: endcase end ;************************************************************************* ; Updates scaling of the plot if the data on it has changed through IDLgrPlot keywords. pro PARgrDisplay::rescale, keep_scale=keep_scale, draw=draw compile_opt IDL2, obsolete, hidden if size(*self.plot_list, /type) ne 0 then $ if obj_valid((*self.plot_list)[0]) then begin if obj_class((*self.plot_list)[0]) eq 'IDLGRPLOT' then begin (*self.plot_list)[0] -> getProperty, data=data self.xrange = get_range(data[0, *]) self.yrange = get_range(data[1, *]) endif else begin ; Contour (*self.plot_list)[0] -> getProperty, xrange=xrange, yrange=yrange self.xrange = xrange self.yrange = yrange endelse endif else return else return n_plots = n_elements((*self.plot_list)) if n_plots gt 1 then $ for i = 1, n_plots-1 do begin if obj_class((*self.plot_list)[i]) eq 'IDLGRPLOT' then begin (*self.plot_list)[i] -> getProperty, data=data xrange = get_range(data[0, *]) yrange = get_range(data[1, *]) endif else (*self.plot_list)[i] -> getProperty, xrange=xrange, yrange=yrange ; Contour ; Find new ranges. They may expand the existing ranges, or may not. xrange = [self.xrange[0] < xrange[0], self.xrange[1] > xrange[1]] yrange = [self.yrange[0] < yrange[0], self.yrange[1] > yrange[1]] self.xrange = xrange self.yrange = yrange ; In case of a PLOT, set its values to right settings. if obj_class((*self.plot_list)[i]) eq 'IDLGRPLOT' then $ (*self.plot_list)[i] -> setProperty, xcoord_conv=self.norm_x, ycoord_conv=self.norm_y, $ xrange=self.x_crange, yrange=self.y_crange $ else (*self.plot_list)[i] -> setProperty, xcoord_conv=self.norm_x, ycoord_conv=self.norm_y ; Contour endfor if not keyword_set(keep_scale) then self -> zoom, /out else self -> zoom, _extra=_extra self.obj_win -> draw, self.view end ;************************************************************************* ; Input parameters must be in data coordinates. pro PARgrDisplay::zoom, xrange=xrange, yrange=yrange, out=out, draw=draw compile_opt IDL2, obsolete, hidden n_x = n_elements(xrange) n_y = n_elements(yrange) if keyword_set(out) then begin if n_x eq n_y then n_x = (n_y = 1) ; If neither one is set, it means we fully zoom out. xrange = self.xrange * (n_x eq 1) + self.x_crange * (n_x eq 0) yrange = self.yrange * (n_y eq 1) + self.y_crange * (n_y eq 0) endif else begin ; Allow zooming along a single axis if only one range is specified. if n_x ne 2 then xrange = self.x_crange else xrange = [xrange[0] < xrange[1], xrange[0] > xrange[1]] if n_y ne 2 then yrange = self.y_crange else yrange = [yrange[0] < yrange[1], yrange[0] > yrange[1]] endelse norm_x = normalize(xrange) norm_y = normalize(yrange) self.norm_x = norm_x self.norm_y = norm_y text_obj = self.model -> get(/all, isa='IDLGRTEXT') poly_obj = self.model -> get(/all, isa='IDLGRPOLYGON') sym_size = [0.006/self.norm_x[1], 0.008/self.norm_y[1]] if obj_valid(text_obj[0]) then $ for i = 0, n_elements(text_obj)-1 do $ text_obj[i] -> setProperty, xcoord_conv=norm_x, ycoord_conv=norm_y if obj_valid(poly_obj[0]) then $ for i = 0, n_elements(poly_obj)-1 do begin poly_obj[i] -> getProperty, uvalue=data temp = xrange[0] > data[0, *] < xrange[1] data[0, *] = temp temp = yrange[0] > data[1, *] < yrange[1] data[1, *] = temp ; The following tries to account for non-convex polygons. But it does not do a very good job. junk = obj_new('IDLgrTessellator') junk -> addPolygon, data print, junk -> tessellate(vertices, poly_descriptor) obj_destroy, junk ; poly_obj[i] -> setProperty, data=data, xcoord_conv=norm_x, ycoord_conv=norm_y poly_obj[i] -> setProperty, data=vertices, polygon=poly_descriptor, xcoord_conv=norm_x, ycoord_conv=norm_y endfor ; We may have chaning number of axes. Get that number. x_index = where(obj_valid(self.x_axes), n_x_axes) y_index = where(obj_valid(self.y_axes), n_y_axes) for i =0, n_x_axes-1 do self.x_axes[x_index[i]] -> setProperty, range=xrange, xcoord_conv=norm_x for i =0, n_y_axes-1 do self.y_axes[y_index[i]] -> setProperty, range=yrange, ycoord_conv=norm_y for i = 0, n_elements((*self.plot_list))-1 do begin if obj_class((*self.plot_list)[i]) eq 'IDLGRPLOT' then begin ; If a PLOT, set properties this way (*self.plot_list)[i] -> getProperty, symbol=symbol if obj_valid(symbol[0]) then for k = 0, n_elements(symbol)-1 do symbol[k] -> setProperty, size=sym_size (*self.plot_list)[i] -> setProperty, xrange=xrange, yrange=yrange, xcoord_conv=norm_x, ycoord_conv=norm_y ; There is a problem in IDL < v. 5.6 - Contour object does not honor X-Y ranges. We comment them out here. endif else (*self.plot_list)[i] -> setProperty, xcoord_conv=norm_x, ycoord_conv=norm_y;, xrange=xrange, yrange=yrange endfor self.x_axes[0] -> getProperty, crange=xc self.y_axes[0] -> getProperty, crange=yc self.x_crange = xc self.y_crange = yc ticks1k, self.y_axes[0] if keyword_set(draw) then self.obj_win -> draw, self.view end ;************************************************************************* ; Will return link value for the object. function PARgrDisplay::is_linked compile_opt IDL2, obsolete, hidden return, self.link end ;************************************************************************* ; This method links several DISPLAYs that have the same LINK flag value, using a plot that has a name ; 'LINK' and supplied region of interest index. I can't come up with how exactly to link DISPLAYs if they ; are used inside widget programs, so I leave LINK only to independent windows. ; Some loops are present here. They are quite short and result from operations on object arrays. pro PARgrDisplay::link_roi, roi compile_opt IDL2, obsolete, hidden Common PARgrDisplay_top_window, obj_list ; Get list of plots from the window that initiated the ROI link. plot_list = *self.plot_list link_id = self.link ; If plot is not linked, exit quietly. if link_id eq 0 then return ; Get information about plots in the window that initiated ROI. n_plots = n_elements(plot_list) names = strarr(n_plots) for i=0, n_plots-1 do begin ; Get list of plot names from Display containing ROI. plot_list[i] -> getProperty, name=temp names[i] = temp endfor for outer_loop=0, n_elements(obj_list)-1 do begin ; We will link over all plots having same, non-empty names. obj_list[outer_loop] -> get_Property, plot_list=curr_plot_list, link=curr_link_id, model=model ; Do not look into Display windows that have different link ID. if curr_link_id ne link_id then continue for i=0, n_elements(curr_plot_list)-1 do begin curr_plot_list[i] -> getProperty, name=temp ; Do not get DATA here to reduce memory use. if temp eq '' then continue ; Ignore plots whos name was not explicitly specified. if strpos(temp, 'Linked') ne -1 then continue ; Ignore links, or it may get confusing. loc = where(names eq temp, count) if count eq 0 then continue ; If the name is not common with ROI-initiator, leave. if count gt 1 then message, 'More than one plot named '+temp+' is present.', /informational ; If a plot is already linked, add a number to name's end to help identify different subsets. added_name = where(strpos(names, 'Linked '+temp) ne -1, count) if count gt 0 then begin added_name = names[added_name[count -1]] last_number = strpos(added_name, ' ', /reverse_offset, /reverse_search) > 0 last_number = strmid(added_name, last_number+1) if (byte(last_number))[0] gt 57b then added_name = 'Linked '+temp+' 1' $ else added_name = 'Linked '+temp+' '+strcompress(fix(last_number)+1, /rem) endif else added_name = 'Linked '+temp ; Done adding number to "linked xxxx ". curr_plot_list[i] -> getProperty, data=data ; Use the same variable to reduce memory use. roi_index = roi -> ContainsPoints(data) ; This is index of points in "LINK" enclosed by ROI. ; Include points inside, on borders and on verteces of ROI. inside_index = where(roi_index gt 0, count, complement=outside_index) if count eq 0 then continue ; If there are no points inside ROI for this NAME, leave. ; Usually, we want to see the linked data. However, we can use the LINK to make some other things. ; If LINK is < 0, we return full length "LINKED" Plot object instead of the short one. if link_id gt 0 then $ data = data[*, inside_index] $ ; Restrict data to ROI index, making shorter data array. else $ data[*, outside_index] = !values.F_NAN ; Invalidate all data points outside ROI, but keep the length. ; Use DISPLAY to add a plot to the linked window. display, data[0, *], data[1, *], psym=4, color=self.roi_color, linestyle=6, oref=obj_list[outer_loop], /over, $ name=added_name endfor endfor ; outer_loop end ;************************************************************************* ; Procedure for printing the selected Display window. pro PARgrDisplay::printout, _extra=_extra compile_opt IDL2, obsolete, hidden ; Get main PARgrDisplay properties. Printer = obj_new('IDLgrPrinter') ; Create a printer object. printer -> getProperty, dim=prt_dim ; Get dimensions of Obj_win, Screen and Printer to preserve the XY ratio of the object plot. self.obj_win -> getproperty, dim=win_dim scr_dim = get_screen_size() ; Take care to preserve PARgrDisplay's X to screen width ratio and aspect ratio. x_ratio = win_dim[0] / scr_dim[0] aspect_ratio = win_dim[1] / win_dim[0] ; Now, scale the plot so that X takes X_RATIO of the printed width and Y is factor of ; ASPECT_RATIO with respect to the new X size. self.model -> scale, x_ratio, aspect_ratio*x_ratio, 1 ; Ready to print. Allow choosing printing options. ok = dialog_printjob(Printer) if ok then Printer -> draw, self.view, vector=0 self.view -> setproperty, location=[0.0, 0.0], dimensions=[0, 0] ; Return the model to the original scale. self.model -> scale, 1/x_ratio, 1/(aspect_ratio*x_ratio), 1 ; Printer object no longer needed. obj_destroy, Printer end ;************************************************************************* ; Allows to copy the object to the clipboard pro PARgrDisplay::copy, _extra=_extra compile_opt IDL2, obsolete, hidden temp = obj_new('IDLgrClipboard', _extra=_extra) temp -> draw, self.view, _extra=_extra obj_destroy, temp end ;************************************************************************* ; Take the contents of current graphic device and make a PNG file out of it. pro PARgrDisplay::make_png, filename=filename compile_opt IDL2, obsolete, hidden if n_elements(filename) eq 0 then filename = dialog_pickfile(/write, filter='*.png', title='Enter PNG file name:', $ file='temp.png') if filename eq '' then return Img = self.obj_win -> read() img -> getProperty, data = img_arr obj_destroy, img write_png, filename, img_arr end ;************************************************************************* ; Allows to save the current display window, with all the data, for future restoring. pro PARgrDisplay::save, _extra=_extra compile_opt IDL2, obsolete, hidden save, self, _extra=_extra end ;************************************************************************* ; This procedure allows to create a window for the restored contents of a previously saved DISPLAY. ; The only two properties of SELF that pertain to GUI are SELF.wid_TLB and SELF.OBJ_WIN, so they will ; need to be replaced with valid references. pro PARgrDisplay::gui, parent=parent, group_leader=group_leader compile_opt IDL2, obsolete, hidden on_error, 1 if obj_class(self) ne 'PARGRDISPLAY' then return ; ++++++ Start with creating a TLB properly +++++++ a_child = 0b ; Assume we are creating independent window. ; Check if Parent was provided. If no, create top base. If yes and Parent is a base, use it. if n_elements(parent) ne 0 then $ ; Check if Parent is even provided... if widget_info(parent, /valid_id) then $ ; Parent is a valid widget... if widget_info(parent, /type) eq 0 then a_child = 1b ; and it is a base. Set a flag variable. if a_child then self.wid_TLB = widget_base(Parent, uvalue=self) $ else begin self.wid_TLB = widget_base(/kbrd_focus_ev, /tlb_size_ev, uvalue=self) if self.wtitle ne '' then widget_control, self.wid_TLB, tlb_set_title=self.wtitle else $ widget_control, self.wid_TLB, tlb_set_title='Display window '+strtrim(self.wid_TLB, 2) ; On Windows (at least), the creation of TLB initiates no WIDGET_KBRD_FOCUS event. We need to make sure ; the first DISPLAY window is recorded in the Common block, so let's call the EVENT procedure from here. ; Also, now OREF is not necessary in a loop calling Display because it will become self-aware immediately. junk = {WIDGET_KBRD_FOCUS} ; Create a dummy structure of the WIDGET_KBRD_FOCUS type. junk.ID = self.wid_TLB & junk.enter = 1 Display_event, temporary(junk) ; Process this "event" immediately. endelse if n_elements(group_leader) ne 0 then $ ; Check if group_leader is even provided... if widget_info(group_leader, /valid_id) then $ ; group_leader is a valid widget... widget_control, self.wid_TLB, group_leader=group_leader ; Use that ID as group leader. ; ++++++ End creating a TLB +++++++ ; Draw area is a child of self.wid_TLB regardless of whether or not parent was provided. self.wid_DRAW = widget_draw(self.wid_TLB, graphics=2, /button_event, xsize=self.xsize, $ ysize=self.ysize, event_pro='Display_event', retain=2, uvalue=self) widget_control, self.wid_DRAW, kill_notify='display_cleanup' ; Need to realize it all now, because we can only get value of realized widget_draw in object mode. widget_control, self.wid_TLB, /realize xmanager, 'display', self.wid_TLB, /No_block widget_control, self.wid_DRAW, get_value=Obj_Win obj_destroy, self.obj_win ; Delete this pre-existing reference... self.obj_win = obj_win ; ...replace it with the newly created one... self.obj_win -> draw, self.view ; ... and update the contents of DISPLAY. self.container -> add, self.obj_win end ;************************************************************************* ; This is an alternative to GUI. It will create an invisible instance of the DISPLAY window, useful for making ; images of windows, etc. Uses an instance of IDLgrBuffer that is not kept as part of object. It will be disposed of ; when the DISPLAY object is destroyed. pro PARgrDisplay::hidden compile_opt IDL2, obsolete, hidden if obj_class(self.obj_win) ne 'IDLGRBUFFER' then begin self.obj_win = obj_new('IDLgrBuffer', dimensions=[self.xsize, self.ysize]) self.container -> add, self.obj_win endif if widget_info(self.wid_TLB, /valid_id) then widget_control, self.wid_TLB, /destroy self.obj_win -> draw, self.view end ;************************************************************************* function PARgrDisplay::isHidden compile_opt IDL2, obsolete, hidden return, obj_class(self.obj_win) eq 'IDLGRBUFFER' end ;************************************************************************* ; There also is a DRAW keyword to SetProperty procedure. This one is short and fast and is used ; when drawing is the only thing done at that particular time. pro PARgrDisplay::draw compile_opt IDL2, obsolete, hidden self.obj_win -> draw, self.view end ;************************************************************************* ; This is a narrow scope method for extracting the data from displayed plots. It can be utilized (at least ; was designed) for extracting data produced by legacy code for plotting, after PLOT command was substituted ; with DISPLAY. Now, this enables us to retain and reuse the data produced by that legacy code. ; Data are returned as a structure whos fields have same names as the plots the data were pulled out from, ; and field values are FLTARR(3, n) where (0, n) is X, (1, n) is Y and (2, n) is Z dimensions of the displayed data. ; Notice that the first field of the returned structure is VOID. function PARgrDisplay::get_data compile_opt IDL2, obsolete, hidden n_plots = n_elements(*self.plot_list) if n_plots eq 0 then return, 0.0 output = create_struct('Void', 'Void') for i = 0, n_plots-1 do begin (*self.plot_list)[i] -> getProperty, data=data, name=name if strlen(name) eq 0 then name = 'Plot '+strcompress(i, /rem) name = str_cleanup(name) output = create_struct(output, "_" + name, data) endfor return, output end ;**************************************************************************** pro PARgrDisplay::dg_out, ps=ps, _extra=_extra compile_opt IDL2, obsolete, hidden if keyword_set(ps) then begin old_device = !d.name set_plot, 'PS' device, /color, _extra=_extra endif n_plots = n_elements(*self.plot_list) if n_plots eq 0 then return c = color() self.main_title -> getProperty, string=Title self.x_title -> getProperty, string=x_Title self.y_title -> getProperty, string=y_Title for i = 0, n_plots-1 do begin if obj_class((*self.plot_list)[i]) ne 'IDLGRPLOT' then continue ; Only process PLOTs. (*self.plot_list)[i] -> getProperty, data=data, color=c1, symbol=symbol, linestyle=linestyle r = where(c[0, *] eq c1[0]) g = where(c[1, *] eq c1[1]) b = where(c[2, *] eq c1[2]) c1 = set_intersection(r, g) c1 = set_intersection(c1, b) if obj_valid(symbol) then symbol -> getProperty, data=psym else psym=0 if linestyle ne 6 then psym = psym*(-1) if i eq 0 then $ plot, data[0, *], data[1, *], color=0, xrange = self.x_crange, yrange = self.y_crange, back=255, $ /xstyle, /ystyle, title=Title, xtitle=x_Title, ytitle=y_Title, psym=psym $ else oplot, data[0, *], data[1, *], color=c1[0], psym=psym endfor if keyword_set(ps) then begin device, /close set_plot, old_device endif end ;**************************************************************************** ; Appends an item to the active Display window. For non-flexible keywords, applies specific actions. pro PARgrDisplay::chooser, x, y, z, legend=legend, text=text, polygon=polygon, xtitle=xtitle, ytitle=ytitle, $ title=title, link=link, wtitle=wtitle, roi_color=roi_color, contour=contour, mp_fit=mp_fit, wipe=wipe, $ coefs_out=coefs_out, _extra=_extra compile_opt IDL2, obsolete, hidden if keyword_set(wipe) then begin ; WIPE is self-containing and specialized; do not do anything else with it. self -> wipe, _extra=_extra return endif if keyword_set(mp_fit) then self -> fit, name=name, coefs_out=coefs_out, _extra=_extra if n_elements(wtitle) ne 0 then widget_control, self.wid_TLB, tlb_set_title=wtitle if n_elements(roi_color) ne 0 then self.roi_color = roi_color if n_elements(link) ne 0 then self.link = link if n_elements(xtitle) ne 0 then self.x_title -> setProperty, strings=xtitle if n_elements(ytitle) ne 0 then self.y_title -> setProperty, strings=ytitle if keyword_set(legend) then self -> legend, title=title, _extra=_extra else $ if n_elements(title) ne 0 then self.main_title -> setProperty, strings=title ; Legend too may have a title if n_elements(text) ne 0 then self -> text, text, _extra=_extra else $ if n_elements(polygon) ne 0 then self -> polygon, x, y, _extra=_extra else $ if n_elements(contour) ne 0 then self -> contour, x, y, z, _extra=_extra else $ self -> plot, x, y, _extra=_extra self.obj_win -> draw, self.view end ;**************************************************************************** pro PARgrDisplay::plot, x_in, y_in, name=name, psym=psym, kill=kill, _extra=_extra compile_opt IDL2, obsolete, hidden if n_elements(name) ne 0 then target = self.model -> getByName(name) if not obj_valid(target) then begin if n_elements(x_in) eq 0 then return target = obj_new('IDLgrPlot') self.model -> add, target, position=0 if (obj_valid(*self.plot_list))[0] then *self.plot_list = [*self.plot_list, target] else *self.plot_list = target endif else if obj_class(target) ne 'IDLGRPLOT' then message, 'Expected IDLgrPLOT, found '+obj_class(target) ; By now we have a valid IDLgrPlot object to work on. if n_elements(y_in) eq 0 then begin if n_elements(x_in) eq 0 then begin ; This may happen if data is not being changed. target -> getproperty, data=temp x = temp[0, *] y = temp[1, *] endif else begin y = x_in x = lindgen(n_elements(y)) endelse endif else begin & x = x_in & y = y_in & endelse if keyword_set(kill) then begin target -> getProperty, symbol=symbol obj_destroy, [target, symbol] *self.plot_list = (*self.plot_list)[where(obj_valid(*self.plot_list)) > 0] self -> legend, /update self -> rescale, _extra=_extra return endif *self.plot_list = (*self.plot_list)[where(obj_valid(*self.plot_list)) > 0] ; In case all plots were killed target -> getProperty, symbol=symbol if n_elements(psym) ne 0 then begin if obj_class(symbol[0]) eq 'IDLGRSYMBOL' then $ for i = 0, n_elements(symbol)-1 do symbol[i] -> setProperty, data=psym $ else begin symbol = obj_new('IDLgrSymbol', psym) self.container -> add, symbol endelse endif target -> setProperty, datax=x, datay=y, _extra=_extra, symbol=symbol, name=name self -> legend, /update self -> rescale, _extra=_extra ticks1k, self.y_axes[0] end ;**************************************************************************** ; Adding text is less trivial than plots, because for multiple line text positioning is required. The only special keyword is ; INTERVAL, which is the size of the line spasing between text lines, in fraction of character height. pro PARgrDisplay::text, strings, name=name, kill=kill, location=location, interval=interval, static=static, $ data=data, normal=normal, _extra=_extra compile_opt IDL2, obsolete, hidden ; If STATIC then use STAITIC_LEVEL model to put text "on glass". Else, let the text be scaled with plots. if keyword_set(static) then begin target_model = self.static_level data = keyword_set(data) ? 1s : 0s ; In Static mode, default to DATA = 0 if nothing is set. endif else begin target_model = self.model data = keyword_set(normal) ? 0s : 1s ; In Dynamic mode, default to DATA = 1 if nothing is set. endelse data_coord = keyword_set(data) ; Just remember this setting for future use if n_elements(name) ne 0 then target = target_model -> getByName(name) new = 0b ; Initialize flag as if existing object is being modified. if not obj_valid(target) then begin target = obj_new('IDLgrText', strings, recompute=2) target_model -> add, target ; Model serves as container for when the objects are killed. new = 1b ; Set flag to indicate that a new object was made. endif else $ if obj_class(target) ne 'IDLGRTEXT' then message, 'Expected IDLgrTEXT, found '+obj_class(target) if keyword_set(kill) then begin ; Kill existing target, if requested. obj_destroy, target return endif ; Get position and contents from the target for calculating new positions. target -> getProperty, location=old_position, strings=old_contents n_old_strings= n_elements(old_contents) ; This is the number of strings in the text before replacing the strings. n_lines = n_elements(strings) ; New n_lines may be different from the old one. if n_elements(interval) ne 1 then interval = data_coord ? 0.25 : 0.02 ; If Data coords, interval is 0.25 ; If this is new object, get its CharSize and create Y offset in case it is an array. if new and n_lines gt 1 then begin charsize = self.obj_win -> GetTextDimensions(target) y_offset = charsize[1] + interval endif else $ ; If object existed and was set to string array, re-use its spacing parameters. if n_old_strings gt 1 then $ y_offset = abs(old_position[1, 1] - old_position[1, 0]) $ ; Get old Y offset ; IDL does not return consistent Y-size of characters for scalar strings vs. array elements. ; Here, we will create a dummy Text object to get the CharSize from it, then kill that object. else begin junk = obj_new('IDLgrText', strings, recompute=2) target_model -> add, junk charsize = self.obj_win -> GetTextDimensions(junk) obj_destroy, junk y_offset = charsize[1] + interval endelse y_offset = findgen(n_lines)*y_offset ; Array of Y offsets is needed for placement of string arrays. if n_elements(location) eq 0 then location = [0.5, 0.5] loc = fltarr(2, n_lines) loc[0, *] = location[0] loc[1, *] = location[1] - y_offset ; Now that we have all needed information, set text parameters. target -> setProperty, strings=strings, location=loc, name=name, _extra=_extra, $ xcoord_conv=data_coord ? self.norm_x : [0,1], $ ; If DATA is not set, use normalized coordinates. ycoord_conv=data_coord ? self.norm_y : [0,1], $ uvalue=data_coord ? 'DATA' : 'NORMALIZED' end ;**************************************************************************** ; The following procedure will analyze the plot_list container, get all needed info and create a legend ; in the form of a separate model (IDLgrLegend) that will be moveable etc. Legend can be positioned programmatically ; using LOCATION keyword set tthe X, Y position of the legend in normalized or data coordinates (determined by ; the setting of DATA keyword). pro PARgrDisplay::legend, title=title, kill=kill, update=update, location=location, data=data, $ font=font, charsize=charsize compile_opt IDL2, obsolete, hidden ; Check if there is a legend already. legend = self.model -> get(isa='IDLGRLEGEND', /all) ; Legend is unique for its own set of plots. if obj_valid(legend) and keyword_set(kill) then begin obj_destroy, legend ; Kill the legend if KILL is set. self.obj_win -> draw, self.view return endif if not obj_valid(legend) then begin ; Only add a legend if it is new. ; If there was no legend, do not create one. Only update if one existed. if keyword_set(update) then return ; Create Font so that it chan be adjusted for the Legend separately from Axes. if size(font, /type) ne 7 then font = 'Helvetica' if n_elements(charsize) eq 0 then charsize = 1. else charsize = float(charsize) default_font = obj_new('IDLgrFont', font, size=12.0*charsize) self.container -> add, default_font legend = obj_new('IDLgrLegend', font=default_font) legend -> setProperty, gap=0.5, glyph_width=3. self.model -> add, legend endif ; Update text properties of the legend. legend -> getProperty, font=default_font if size(font, /type) eq 7 then default_font -> setProperty, name=font if n_elements(charsize) ne 0 then default_font -> setProperty, size=12.0*charsize if n_elements(title) ne 0 then begin ; If legend_title was provided, create title object and store it in Container. title_obj = obj_new('IDLgrText', title, _extra=_extra, name='legend_title', recompute=2, font=default_font) self.container -> add, title_obj endif if (obj_valid(*self.plot_list))[0] eq 0 then begin ; If the last plot was removed from DISPLAY, remove all legend information but keep the legend there. ; This way, if a user adds a plot to DISPLAY, it will automatically show up in the legend. legend -> setProperty, item_name='', item_object=obj_new(), title=title_obj return ; If no plots are present, quit. endif else count = n_elements(*self.plot_list) item_color = fltarr(3, count) item_name = strarr(count) item_linestyle = bytarr(count) item_object = objarr(count) for i=0, count-1 do begin ; Get information from all plot objects in DISPLAY. (*self.plot_list)[i] -> getProperty, color=color, symbol=symbol, linestyle=linestyle, name=name item_color[*, i] = color if name ne '' then item_name[i] = name else item_name[i] = 'Plot '+strcompress(i, /rem) item_linestyle[i] = linestyle if obj_valid(symbol[0]) then item_object[i] = symbol[0] endfor ; If a name was provided, do not use it. Legend does not have a separate name. ; if (where(tag_names(_extra) eq 'NAME'))[0] ne -1 then _extra.name = 'LEGEND' ; Set all properties of the legend according to the currend window contents. legend -> setProperty, item_name=item_name, item_color=item_color, item_linestyle=item_linestyle, $ item_object=item_object, /select_target, title=title_obj;, _extra=_extra if n_elements(location) eq 2 then begin ctm = legend -> getctm() ; Obtain current location of the legend. if keyword_set(data) then begin delta_x = (location[0] - self.x_crange[0])/(self.x_crange[1] - self.x_crange[0]) -ctm[3, 0] delta_y = (location[1] - self.y_crange[0])/(self.y_crange[1] - self.y_crange[0]) -ctm[3, 1] endif else begin delta_x = location[0] delta_y = location[1] endelse legend -> translate, delta_x, delta_y, 0 endif end ;**************************************************************************** pro PARgrDisplay::polygon, x, y, name=name, kill=kill, _extra=_extra compile_opt IDL2, obsolete, hidden if n_elements(name) ne 0 then target = self.model -> getByName(name) if not obj_valid(target) then begin if n_elements(x) eq 0 then return else $ if n_elements(y) eq 0 then begin y = x x = indgen(n_elements(y)) endif target = obj_new('IDLgrPolygon') self.model -> add, target endif else if obj_class(target) ne 'IDLGRPOLYGON' then message, 'Expected IDLgrPolygon, found '+obj_class(target) ; By now we have a valid IDLgrPolygon object to work on. if keyword_set(kill) then begin obj_destroy, target return endif if n_elements(x) * n_elements(y) ne 0 then data=transpose([[x], [y]]) else target -> getProperty, data=data data = float(data) ; In case integer ranges were specified on command line ; The following tries to account for non-convex polygons. But it does not do a very good job. junk = obj_new('IDLgrTessellator') junk -> addPolygon, data print, junk -> tessellate(vertices, poly_descriptor, /quiet) obj_destroy, junk target -> setProperty, data=vertices, name=name, xcoord_conv=self.norm_x, ycoord_conv=self.norm_y, $ uvalue=data, polygon=poly_descriptor, _extra=_extra ; target -> setProperty, data=data, name=name, xcoord_conv=self.norm_x, ycoord_conv=self.norm_y, $ ; uvalue=data, _extra=_extra self -> zoom, xrange=self.x_crange, yrange=self.y_crange ; Set polygon boundaries to respect the current window size and position end ;**************************************************************************** ; Adds a contour plot to the Display window. This section uses ideas and code from XCONTOUR and VCOLORBAR by ; FANNING SOFTWARE CONSULTING, David Fanning, Ph.D. ; 1645 Sheely Drive ; Fort Collins, CO 80526 USA ; Phone: 970-221-0438 ; E-mail: davidf@dfanning.com ; Coyote's Guide to IDL Programming: http://www.dfanning.com pro PARgrDisplay::contour, x, y, z, name=name, kill=kill, nlevels=nlevels, ctable=ctable, _extra=_extra compile_opt IDL2, obsolete, hidden if size(z, /n_dimensions) ne 2 then if size(x, /n_dimensions) eq 2 then z = temporary(x) else z = LoadData(2) ; Get the contour if a name was provided, or create a new one. if n_elements(name) ne 0 then target = self.model -> getByName(name) if not obj_valid(target) then begin if n_params() eq 0 then return ; No data and existing IDLgrContour not found, just exit. target = obj_new('IDLgrContour') self.model -> add, target, position=0 if (obj_valid(*self.plot_list))[0] then *self.plot_list = [*self.plot_list, target] else *self.plot_list = target endif else if obj_class(target) ne 'IDLGRCONTOUR' then message, 'Expected IDLgrCONTOUR, found '+obj_class(target) ; Bt now, we have an IDLgrContour to work on. if keyword_set(kill) then begin ; Kill object if requested. obj_destroy, target *self.plot_list = (*self.plot_list)[where(obj_valid(*self.plot_list)) > 0] self -> rescale, _extra=_extra return endif *self.plot_list = (*self.plot_list)[where(obj_valid(*self.plot_list)) > 0] ; In case all plots were killed ; Get LEVELS, as they ar not calculated from NLEVELS. Default NLEVELS is 30. if n_elements(nlevels) eq 0 then nlevels = 30.0 step = (Max(z) - Min(z)) / nlevels levels = Indgen(nlevels) * step + Min(z) ; Calculate the contour colors. colorStep = Fix(255/nlevels) contourColors = BIndGen(3, nlevels) col = PARgrColor() ; PARgrColor is a simple function for getting a 256 element Rainbow color table. FOR j=0, nlevels-1 DO contourColors[*,j] = col[*, j*colorStep] ; Create a contour plot object. Have to add an extra level ; to the contour levels to get the top color to plot. Bug? target -> setProperty, data_values=z, geomx=x, geomy=y, GeomZ=-0.01, Color=[255, 255, 0], /Planar, $ C_Color=contourColors, C_Value=levels, name=name, _extra=_extra ; Add the contour to the model and plot list, rescale. self -> rescale, _extra=_extra end ;**************************************************************************** ; Method allows to integrate MPFIT program into Display for interactive fitting to displayed plots. pro PARgrDisplay::fit, name=name, fit_name=fit_name, fit_func=fit_func, coefs_in=coefs_in, $ coefs_out=coefs_out, _extra=_extra compile_opt IDL2, obsolete, hidden if n_elements(name) ne 0 then target = self.model -> getByName(name) else message, 'Must use NAMED plot to fit!' if not obj_valid(target) then message, 'Invalid plot name!' if obj_class(target) ne 'IDLGRPLOT' then message, 'Expected IDLgrPLOT, found '+obj_class(target) if n_elements(fit_name) eq 0 then message, 'Provide a NAME for the fit data!' else begin fit = self.model -> getByName(fit_name) if not obj_valid(fit) then begin fit = obj_new('IDLgrPlot', name=fit_name, _extra=_extra) self.model -> add, fit, position=0 *self.plot_list = [*self.plot_list, fit] endif else $ if obj_class(target) ne 'IDLGRPLOT' then message, 'Expected IDLgrPLOT, found '+obj_class(target) endelse target -> getProperty, data = xy_source x = xy_source[0, *] y = xy_source[1, *] index = where(finite(x) and finite(y)) ; MPFIT does not accept NaN. Eliminate them first. x = x[index] y = y[index] Coefs_out = MPFIT(fit_func, Coefs_In, FUNCTARGS={x : x, y : y}, /quiet, _extra=_extra) index = sort(x) ; If X is not sorted, the fitted line may have zig-zags when plotted. x = x[index] y = call_function(Fit_Func, Coefs_out, x=x) self -> plot, x, y, name=fit_name, _extra=_extra end ; ************************************************************************* pro PARgrDISPLAY::grids, gridstyle=gridstyle, color=color compile_opt IDL2, obsolete, hidden ; Gridding axes are number 2 in self.x_axes and self.y_axes arrays. if n_elements(color) eq 0 then color = [100,100,255] self.x_axes[2] -> setProperty, minor = (gridstyle < 0), gridstyle=abs(gridstyle), ticklen=1, subticklen=1, color=color self.y_axes[2] -> setProperty, minor = (gridstyle < 0), gridstyle=abs(gridstyle), ticklen=1, subticklen=1, color=color self.obj_win -> draw, self.view end ; ************************************************************************* ; Pavel Romashkin, November 2002 ; This procedure uses a Linked plot to allow to delete data points form a plot in DISPLAY window. pro PARgrDISPLAY::wipe, name=name compile_opt IDL2, obsolete, hidden ; The NAME must be a linked plot, else just quit. if n_elements(name) eq 0 then message, 'No name is provided for Wiping data points' if strpos(name, 'Linked') eq -1 then message, 'Name to wipe must start with "Linked"' ; The LINK property must be < 0 to provide full length useful linked arrays. If LINK is not< 0, return. ; It is possible to get intersections of X, Y in the Linked plot with the Data plot, but this is a lot faster and simplier. if self.link ge 0 then message, 'Link must be < 0 for Wiping data points' ; Name of the main plot is the same as Linked plot. plot_name = strmid(name, 7) main_plot = self.model -> getByName(plot_name) link_plot = self.model -> getByName(name) link_plot -> getProperty, data=link_data main_plot -> getProperty, data=data data[1, where(finite(link_data[1, *]))] = !values.F_NAN main_plot -> setProperty, datax=data[0, *], datay=data[1, *] display, /kill, /over, name=name, oref=self end ;**************************************************************************** ; Written by: Pavel A. Romashkin, 08-2001 ; Universal procedure for setting properties. DOES NOT TOLERATE KEYWORD ABBREVIATIONS. pro PARgrDisplay::set_property, _extra=_extra compile_opt IDL2, obsolete, hidden n_items = n_tags(_extra) if n_items eq 0 then return tags = tag_names(_extra) all_tags = tag_names({PARgrDisplay}) for i = 0, n_items-1 do begin loc = (where(all_tags eq tags[i], count))[0] if count eq 1 then if size(self.(loc), /type) eq 10 then $ *self.(loc) = _extra.(i) else self.(loc) = _extra.(i) endfor end ;**************************************************************************** ; Written by: Pavel A. Romashkin, 08-2001 ; Universal function for retrieving just one property of the object. This is useful when only one object ; property is needed at a time, e.g. to use as an argument. DOES NOT TOLERATE KEYWORD ABBREVIATIONS. function PARgrDisplay::return_property, _extra=_extra compile_opt IDL2, obsolete, hidden tags = tag_names(_extra) all_tags = tag_names({PARgrDisplay}) loc = (where(all_tags eq tags[0], count))[0] if count eq 0 then return, 0 if size(self.(loc), /type) eq 10 then return, *self.(loc) else $ return, self.(loc) end ;**************************************************************************** ; Written by: Pavel A. Romashkin, 08-2001 ; Universal procedure for retrieving more than one property of the object at a time. ; DOES NOT TOLERATE KEYWORD ABBREVIATIONS. ; This method creates a temporary procedure file which is getting deleted after it is compiled. ; This only gets done once per session in the INIT function. The reason for this is that there seems ; to be no way to access variables passes via keywords without explicitly defining all keywords ; in that procedure. Using Routine_names, this process is automated, so there is no need to alter ; GET_PROPERTY should the object itself change. pro PARgrDisplay::get_property, _ref_extra=_extra, out=out compile_opt IDL2, obsolete, hidden if (where(routine_info() eq 'PARGRDISPLAY::AGET'))[0] eq -1 then begin header = 'pro PARgrDisplay::aget,in_tags' keywords = tag_names({PARgrDisplay}) for i = 0, n_elements(keywords)-1 do header = header +','+ keywords[i]+'='+keywords[i] buffer = strarr(10) buffer[0] = 'compile_opt IDL2, obsolete, hidden' buffer[1] = 'all_tags = tag_names({PARgrDisplay})' buffer[2] = 'n_items = n_elements(in_tags)' buffer[3] = 'for i = 0, n_items-1 do begin' buffer[4] = ' loc = (where(all_tags eq in_tags[i], count))[0]' buffer[5] = ' if count eq 0 then continue' buffer[6] = ' if size(self.(loc), /type) eq 10 then junk = routine_names(in_tags[i], $' buffer[7] = ' *self.(loc), store=0) else junk = routine_names(in_tags[i], self.(loc), store=0)' buffer[8] = 'endfor' buffer[9] = 'end' openw, unit, 'pargrdisplay__aget.pro', /get_lun printf, unit, header, buffer, format='(a)' free_lun, unit resolve_routine, 'pargrdisplay::aget' file_delete, 'pargrdisplay__aget.pro' endif self -> aget, _extra, _extra=_extra end ;**************************************************************************** ; Written by: Pavel A. Romashkin, 08-2001 ; Universal procedure for setting properties. DOES NOT TOLERATE KEYWORD ABBREVIATIONS. pro PARgrDisplay::get_property_structure, _extra=_extra, out=out compile_opt IDL2, obsolete, hidden out = _extra n_items = n_tags(out) tags = tag_names(out) all_tags = tag_names({PARgrDisplay}) for i = 0, n_items-1 do begin loc = (where(all_tags eq tags[i], count))[0] if count eq 1 then if size(self.(loc), /type) eq 10 then $ out.(i) = *self.(loc) else out.(i) = self.(loc) endfor end ;************************************************************************* ; Initializes the object itself and creates widget system for displaying object data. function PARgrDisplay::init, xsize=xsize, ysize=ysize, font=font, wtitle=wtitle, charsize=charsize, $ margin_left=margin_left, margin_bottom=margin_bottom compile_opt IDL2, obsolete, hidden ; Provide error handling. Catch, Err_code if (Err_code ne 0) then begin print,'PARgrDisplay::init error caught:' help, /last_message catch, /cancel return, 0 endif if n_elements(xsize) eq 0 then xsize = 600 if n_elements(ysize) eq 0 then ysize = 350 self.xsize = xsize self.ysize = ysize if n_elements(wtitle) ne 0 then self.wtitle = wtitle ; Make it easy to assign values to pointer fields. temp = n_tags({PARgrDisplay}) for i=0, temp-1 do if size(self.(i), /type) eq 10 then self.(i) = ptr_new(/allocate) ;---------------------------- OBJECT INITIALIZATION START ---------------------------- ; Initialize containers. self.trash = obj_new('IDL_Container') self.container = obj_new('IDL_Container') self.roi_color = [255,0,0] ; Default color is red. ; Set object ranges to proper values. self.xrange = (self.yrange = [0.0, 1.0]) self.norm_x = normalize(self.xrange) self.norm_y = normalize(self.yrange) self.x_crange = self.xrange self.y_crange = self.yrange ; Need to create new Model, View etc. objects. self.model = obj_new('IDLgrModel') self.static_level = obj_new('IDLgrModel') ; Create the view object. self.view = obj_new('IDLgrView', viewplane_rect=[-0.1, -0.1, 1.12, 1.16], location=[0.0, 0.0]) ; Create window-wide font, if requested. If not requested, default of Helvetica 12 pt will be used. if size(font, /type) ne 7 then font = 'Helvetica' ; Adjust viewport for the font size. if n_elements(charsize) ne 0 then begin charsize = float(charsize) default_font = obj_new('IDLgrFont', font, size=12.0*charsize) ; Make sure margins are wide enough for the fonts. if n_elements(margin_left) eq 0 then margin_left = -0.1*charsize if n_elements(margin_bottom) eq 0 then margin_bottom = -0.1*charsize self.view -> setproperty, viewplane_rect=[margin_left, margin_bottom, $ 1.02 + abs(margin_left), 1+.06*charsize+ abs(margin_bottom)] endif else default_font = obj_new('IDLgrFont', font, size=12.0) self.container -> add, default_font ; Create and initialize axes. self.x_axes[0] = obj_new('IDLgrAxis', 0, ticklen=0.03, name='X_AXIS', location=[1000, 0, 0.01], /exact) ; X self.y_axes[0] = obj_new('IDLgrAxis', 1, ticklen=0.03, name='Y_AXIS', location=[0, 1000, 0.01], /exact) ; Y self.x_axes[1] = obj_new('IDLgrAxis', 0, ticklen=0.03, tickdir=1, loc=[1000, 1, 0], /notext, /exact) ; X_R self.y_axes[1] = obj_new('IDLgrAxis', 1, ticklen=0.03, tickdir=1, loc=[1, 1000, 0], /notext, /exact) ; Y_T ; Prepare axes for grid lines. self.x_axes[2] = obj_new('IDLgrAxis', 0, ticklen=0.03, tickdir=1, loc=[1000, 1, 0], /notext, /exact) ; X_Grid self.y_axes[2] = obj_new('IDLgrAxis', 1, ticklen=0.03, tickdir=1, loc=[1, 1000, 0], /notext, /exact) ; Y_Grid ; Only place labels on the left and bottom axes. self.x_axes[0] -> getProperty, ticktext=x_tick_labels self.y_axes[0] -> getProperty, ticktext=y_tick_labels x_tick_labels -> setProperty, recompute=2, font=default_font y_tick_labels -> setProperty, recompute=2, font=default_font ; Create default axis titles. self.x_title = obj_new('IDLgrText', 'X title', font=default_font, recompute=2) self.y_title = obj_new('IDLgrText', 'Y title', font=default_font, recompute=2) self.x_axes[0] -> setProperty, title=self.x_title, tickformat='(G0)' self.y_axes[0] -> setProperty, title=self.y_title, tickformat='(G0)' ; Create plot title. self.main_title = obj_new('IDLgrText', font=default_font, recompute=2, location=[0.5, 1.02], align=0.5) ; Set axes ranges and remove extra zeros from tick labels if they are present. x_index = where(obj_valid(self.x_axes), n_x_axes) y_index = where(obj_valid(self.y_axes), n_y_axes) for i =0, n_x_axes-1 do self.x_axes[x_index[i]] -> setProperty, range=self.xrange, xcoord_conv=self.norm_x, ycoord_conv=self.norm_y for i =0, n_y_axes-1 do self.y_axes[y_index[i]] -> setProperty, range=self.yrange, xcoord_conv=self.norm_x, ycoord_conv=self.norm_y ; Add atomic objects to the model. self.model -> add, self.x_axes[x_index] self.model -> add, self.y_axes[y_index] self.static_level -> add, self.main_title self.view -> add, self.model self.view -> add, self.static_level ;---------------------------- OBJECT INITIALIZATION END ---------------------------- self -> get_property, void=void return, 1 end ;************************************************************************* ; Universal cleanup routine. Kills everything it finds, but will miss nested pointers or ORefs. pro PARgrDisplay::cleanup compile_opt IDL2, obsolete, hidden ;Release all pointers and object references that are present in PARgrDisplay object. tags = n_tags({PARgrDisplay}) for i=0, tags-1 do begin if size(self.(i), /type) eq 10 then ptr_free, self.(i) if size(self.(i), /type) eq 11 then obj_destroy, self.(i) endfor end ;************************************************************************* ; The following pro will delete all plots from the provided DISPLAY. This is useful when a Display ; needs to be emptied in a GUI environment. pro PARgrDisplay::flush_plots compile_opt IDL2, obsolete, hidden if n_elements(*self.plot_list) ne 0 then obj_destroy, *self.plot_list end ;************************************************************************* ; PARgrDisplay definition procedure. pro PARgrDisplay__define compile_opt IDL2, obsolete, hidden temp = fltarr(2) i_a2 = intarr(2) result = {PARgrDisplay, $ ; Name of class container : obj_new(), $ ; Container for easy clean-up link : 0s, $ ; Flag indicating that the object is to be used for linking of ROIs. main_title : obj_new(), $ ; Main title, positioned at top middle of the window model : obj_new(), $ ; Model object mouse_button : 0b, $ ; Mouse button type - 1 right, 2 middle, 4 left. mouse_press : i_a2, $ ; Mouse press in screen coordinates mouse_press_data : fltarr(3), $; Mouse press in data coordinates norm_x : temp, $ ; XCOORD_CONV setting norm_y : temp, $ ; YCOORD_CONV setting obj_win : obj_new(), $ ; Draw window reference plot_list : ptr_new(), $ ; List of all plots for easy access roi_color : bytarr(3), $ ; Color of the ROI. roi_object : obj_new(), $ ; Temporary storage for ROI or zooming rectangle. static_level : obj_neW(), $ ; IDLgrModel for storing Title, etc. that are static in the window. trash : obj_new(), $ ; Container object for disposable items. uvalue : ptr_new(), $ ; User value view : obj_new(), $, ; View - for easy drawing. wid_DRAW : 0L, $ ; Draw widget ID wid_Draw_CON : 0L, $ ; Draw widget context menu ID wid_TLB : 0L, $ ; Top base widget ID wtitle : '', $ ; Title of the window xrange : temp, $ ; X range vector xsize : 0s, $ ; Horizontal size of the DISPLAY window x_axes : objarr(3), $ ; List of X axes for easy access x_crange : temp, $ ; Actual axis range x_title : obj_new(), $ ; Title for X axis yrange : temp, $ ; Y range vector ysize : 0s, $ ; Vertical size of the DISPLAY window y_axes : objarr(3), $ ; List of Y axes for easy access y_crange : temp, $ ; Actual axis range y_title : obj_new()} ; Title for Y axis end ;************************************************************************* ;Placed in here to avoid need for separate download :-) function get_range, in_array compile_opt IDL2, obsolete, hidden arr_min = min(in_array, max=arr_max, /nan) return, [arr_min, arr_max] end ;************************************************************************* ;Placed in here to avoid need for separate download :-) ;The following function is provided by Dr. David Fanning of Fanning Software Consulting. ;See www.dfanning.com for details of copyright and GNU license. FUNCTION Normalize, range, Position=position compile_opt IDL2, obsolete, hidden On_Error, 1 IF N_Params() EQ 0 THEN Message, 'Please pass range vector as argument.' IF (N_Elements(position) EQ 0) THEN position = [0.0, 1.0] ELSE $ position=Float(position) range = Float(range) scale = [((position[0]*range[1])-(position[1]*range[0])) / $ (range[1]-range[0]), (position[1]-position[0])/(range[1]-range[0])] RETURN, scale END ;************************************ ; By Pavel A. Romashkin, NOAA-CMDL, 1999 ; This routine will shorten tick labels on a given object axis. ; If AX_MAX (float) is over 1000. for AXIS ('IDLgrAxis' instance), then remove 3 zeros from tick marks ; and append ' x1000' to existing axis title. If TITLE is provided, it will contain ' x1000'. ;Placed in here to avoid need for separate download :-) pro ticks1k, the_AXIS, AX_MAX, A_TITLE, format=format if n_elements(format) eq 0 then format = '(F5.1)' the_AXIS -> getProperty, tickvalues=tick_text, ticktext=tick_oref, title=axis_title_oref, $ crange=crange ax_max = crange[1] ; Get axis title, if present. if obj_valid(axis_title_oref) then $ axis_title_oref -> getproperty, string=axis_title $ else axis_title = '' ; Set axis_title to A_TITLE, if it was supplied. If not or if blank, use old title. if size(a_title, /type) eq 7 then $ if strlen(a_title) gt 0 then axis_title = a_title if AX_MAX ge 1000. then begin ; This means, divide tick marks by 1000 and change axis label. tick_text = string(tick_text / 1000., format=format) tick_oref -> setproperty, string=tick_text if strpos(axis_title, ' x1000') eq -1 then axis_title = axis_title+' x1000' endif else begin; Maybe it was 1k scaled; check and remove, because limit is less than 1k. loc = strpos(axis_title, 'x1000') if loc ne -1 then axis_title = strmid(axis_title, 0, loc) endelse ; Set the title to its value. if obj_valid(axis_title_oref) then axis_title_oref -> setProperty, string=axis_title end ;************************************************************************* ; Create a color vector to be used by OG plotting routines function PARgrColor compile_opt idl2 tvlct, r,g,b,/get return, transpose([[r],[g],[b]]) end ;************************************************************************* ; Procedure to allow calling Cleanup method on the PARgrDisplay by Xmanager. This is needed ; because an object method can is not allowed to be use as a Widget clean-up routine. pro display_cleanup, wid_ID Common PARgrDisplay_top_window, got_focus widget_control, wid_ID, get_uval=Object ; Get object reference and destroy it. ; If the cleanup was called because Display was Hidden, don't kill the object. if (Object -> isHidden()) eq 0 then begin obj_destroy, Object if n_elements(got_focus) ne 0 then $ got_focus = got_focus[where(obj_valid(got_focus)) > 0] ; Clean up the common block. endif end ;************************************************************************* ; Dummy procedure to isolate the user from the object syntax of PARgrDisplay. ; Tracking of independent windows is provided through common block. Update of common block ; occurs in the event procedure, when focus changes. Therefore, daughter windows provide no updates ; to common block, because they can not generate focus events. This is the desired behavior. pro display, x, y, z, over=over, printout=printout, font=font, charsize=charsize, ALL=ALL, $ parent=parent, group_leader=group_leader, oref=oref, $ ; These are for programmatic access to a Display window. copy=copy, save_file=save_file, restore_file=restore_file, dg_out=dg_out, $ coefs_out=coefs_out, xrange=xrange, yrange=yrange, out=out, no_gui=no_gui, make_png=make_png, $ margin_left=margin_left, margin_bottom=margin_bottom, gridstyle=gridstyle, _extra=_extra compile_opt IDL2, obsolete, hidden Common PARgrDisplay_top_window, temp Catch, Err_code ; Provide error handling. if (Err_code ne 0) then begin print,'DISPLAY error caught:' help, /last_message catch, /cancel return endif if keyword_set(no_gui) and (not arg_present(oref)) then message, 'Must provide OREF keyword for hidden DISPLAY object.' ; Apply ALL keyword first. Just call DISPLAY recursively. We do not expect this keyword used for anything ; other than property setting, so we do not pass X or Y, or any other such parameters or keywords. if keyword_set(all) then begin index_displays = where(obj_valid(temp), n_displays) if n_displays eq 0 then return for i = 0, n_displays-1 do Display, /over, all=0, _extra=_extra return endif filename = size(restore_file, /type) ; Restore a saved DISPLAY. if filename ne 0 then begin junk = n_tags({IDLgrLegend}) ; Create the right definition of IDLgrLegend junk = n_tags({PARgrDISPLAY}) ; Create the right definition of PARgrDISPLAY if filename eq 7 then filename = restore_file else filename = dialog_pickfile(filter='*.dis') restore, filename, /relaxed if keyword_set(no_gui) then self -> hidden else self -> gui oref = self return ; No other action is performed, because you need to see what you have first. endif ; Display may be used in a widget application. In this case, OREF is a valid reference at this point, and we need ; not use the TEMP variable. If OVER is not set, all plots need to be removed first; otherwise, we will just add a plot. oref_was_present = obj_valid(oref) if oref_was_present and (keyword_set(OVER) eq 0) then oref -> flush_plots if n_elements(temp) ne 0 then $ if obj_valid(temp[0]) and (not obj_valid(oref)) then oref = temp[0] ; if oref was passed in as a parameter, keep it ; Create new instance if required, or used existing one to add items. Generate error on a mistake. if not keyword_set(over) then begin if oref_was_present eq 0 then oref = obj_new('PARgrDisplay', font=font, charsize=charsize, $ _extra=_extra, margin_left=margin_left, margin_bottom=margin_bottom) ; Object onstance is not visualized at this point yet. Call GUI method to make it visible. if keyword_set(no_gui) then oref -> hidden else oref -> gui, parent=parent, group_leader=group_leader endif else if not obj_valid(oref) then message, 'No window exists to overplot.' oref -> chooser, x, y, z, coefs_out=coefs_out, font=font, charsize=charsize, _extra=_extra if keyword_set(xrange) or keyword_set(yrange) or keyword_set(out) then $ oref -> zoom, xrange=xrange, yrange=yrange, /draw, out=out if keyword_set(copy) then oref -> copy, _extra=_extra if keyword_set(printout) then oref -> printout, _extra=_extra if keyword_set(save_file) then oref -> save, _extra=_extra if keyword_set(dg_out) then oref -> dg_out, _extra=_extra if keyword_set(no_gui) then oref -> hidden if keyword_set(make_png) then oref -> make_png, _extra=_extra if n_elements(gridstyle) ne 0 then oref -> grids, gridstyle=gridstyle end ;*************************************************************************