; docformat = 'rst' ; ; NAME: ; cgSurf ; ; PURPOSE: ; The purpose of cgSurf is to create a wrapper for the traditional IDL graphics ; commands, Surface and Shade_Surf. The primary purpose of this is to create surface ; commands that work and look identically both on the display and in PostScript files. ; ;******************************************************************************************; ; ; ; Copyright (c) 2010, by Fanning Software Consulting, Inc. All rights reserved. ; ; ; ; Redistribution and use in source and binary forms, with or without ; ; modification, are permitted provided that the following conditions are met: ; ; ; ; * Redistributions of source code must retain the above copyright ; ; notice, this list of conditions and the following disclaimer. ; ; * Redistributions in binary form must reproduce the above copyright ; ; notice, this list of conditions and the following disclaimer in the ; ; documentation and/or other materials provided with the distribution. ; ; * Neither the name of Fanning Software Consulting, Inc. nor the names of its ; ; contributors may be used to endorse or promote products derived from this ; ; software without specific prior written permission. ; ; ; ; THIS SOFTWARE IS PROVIDED BY FANNING SOFTWARE CONSULTING, INC. ''AS IS'' AND ANY ; ; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ; ; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT ; ; SHALL FANNING SOFTWARE CONSULTING, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, ; ; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED ; ; TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; ; ; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ; ; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ; ; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ; ; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ; ;******************************************************************************************; ; ;+ ; The purpose of cgSurf is to create a wrapper for the traditional IDL graphics ; commands, Surface and Shade_Surf. The primary purpose of this is to create surface ; commands that work and look identically both on the display and in PostScript files. ; ; :Categories: ; Graphics ; ; :Params: ; data: in, required, type=any ; A two-dimensional array of data to be displayed. ; x: in, optional, type=any ; A vector or two-dimensional array specifying the X coordinates of the ; surface grid. ; y: in, optional, type=any ; A vector or two-dimensional array specifying the Y coordinates of the ; surface grid. ; ; :Keywords: ; addcmd: in, optional, type=boolean, default=0 ; Set this keyword to add the command to an cgWindow. Setting this keyword ; automatically sets the WINDOW keyword, but the command does not erase the ; graphics window as it would normally. ; axiscolor: in, optional, type=string/integer, default='opposite' ; If this keyword is a string, the name of the axis color. ; Otherwise, the keyword is assumed to be a color index into the current color table. ; axescolor: in, hidden, type=string/integer ; Provisions for bad spellers. ; background: in, optional, type=string/integer, default='background' ; If this keyword is a string, the name of the background color. ; Otherwise, the keyword is assumed to be a color index into the current color table. ; bottom: in, optional, type=string/integer, default='black' ; If this keyword is a string, the name of the bottom color. By default, same as COLOR. ; Otherwise, the keyword is assumed to be a color index into the current color table. ; charsize: in, optional, type=float, default=cgDefCharSize*1.25 ; The character size for axes annotations. Uses cgDefCharSize()*1.25 to select default ; character size, unless !P.Charsize is set, in which case !P.Charsize*1.25 is always used. ; color: in, optional, type=string/integer, default='blu6' ; If this keyword is a string, the name of the data color. By default, "BLU6". ; Otherwise, the keyword is assumed to be a color index into the current color table. ; elevation_shading: in, optional, type=boolean, default=0 ; Set this keyword to put elevation shading into effect for the surface. ; font: in, optional, type=integer, default=-1 ; The type of font desired. If undefined, and the current graphics device is PostScript, ; the FONT keyword will be set to 1, indicating true-type fonts. The FONT keyword must ; be set to -1 (Hershey fonts) or 1 (true-type fonts) for surface annotations to be ; rotated correctly in PostScript output. ; layout: in, optional, type=intarr(3) ; This keyword specifies a grid with a graphics window and determines where the ; graphic should appear. The syntax of LAYOUT is three numbers: [ncolumns, nrows, location]. ; The grid is determined by the number of columns (ncolumns) by the number of ; rows (nrows). The location of the graphic is determined by the third number. The ; grid numbering starts in the upper left (1) and goes sequentually by column and then ; by row. ; noerase: in, optional, type=boolean, default=0 ; Set this keyword to prevent the window from erasing the contents before displaying ; the surface plot. ; outfilename: in, optional, type=string ; If the `Output` keyword is set, the user will be asked to supply an output ; filename, unless this keyword is set to a non-null string. In that case, the ; value of this keyword will be used as the filename and there will be no dialog ; presented to the user. ; output: in, optional, type=string, default="" ; Set this keyword to the type of output desired. Possible values are these:: ; ; 'PS' - PostScript file ; 'EPS' - Encapsulated PostScript file ; 'PDF' - PDF file ; 'BMP' - BMP raster file ; 'GIF' - GIF raster file ; 'JPEG' - JPEG raster file ; 'PNG' - PNG raster file ; 'TIFF' - TIFF raster file ; ; Or, you can simply set this keyword to the name of the output file, and the type of ; file desired will be determined by the file extension. If you use this option, the ; user will not be prompted to supply the name of the output file. ; ; All raster file output is created through PostScript intermediate files (the ; PostScript files will be deleted), so ImageMagick and Ghostview MUST be installed ; to produce anything other than PostScript output. (See cgPS2PDF and cgPS_Close for ; details.) And also note that you should NOT use this keyword when doing multiple ; plots. The keyword is to be used as a convenient way to get PostScript or raster ; output for a single graphics command. Output parameters can be set with cgWindow_SetDefs. ; palette: in, optional, type=byte ; Set this keyword to a 3 x N or N x 3 byte array containing the RGB color vectors ; to be loaded before the surface is displayed. Such vectors can be obtained, for ; example, from cgLoadCT with the RGB_TABLE keyword: ; ; cgLoadCT, 33, RGB_TABLE=palette ; cgSurf, cgDemoData(2), PALETTE=palette, /Elevation ; ; rotx: in, optional, type=float, default=30 ; The rotation about the X axis. ; rotz: in, optional, type=float, default=30 ; The rotation about the Z axis. ; shaded: in, optional, type=boolean, default=0 ; Set this keyword if you wish to display a shaded surface. To display shaded surfaces ; in a device-independent way, the shading values are confined to indices 0 to 253 with ; SET_SHADING, and the background color is placed in color index 254. The color table vectors ; are reduced to 254 elements when this happens. This all happens behind the stage, ; and the original color table is restore upon exit. Because I can't tell how many values ; SET_SHADING is using on entering the program, I just set it back to its default 256 values ; on exiting the program. ; shades: in, optional, type=byte ; Set this keyword to a byte scaled 2D array of the same size as data to shade the surface ; with these color indices. ; skirt: in, optional, type=any ; Set this keyword to a Z value where a skirt will be drawn for the surface. ; title: in, optional, type=string ; The title of the plot. It will be written "flat to the screen", rather than rotated. ; traditional: in, optional, type=boolean, default=0 ; If this keyword is set, the traditional color scheme of a black background for ; graphics windows on the display is used and PostScript files always use a white background. ; tlocation: in, optional, type=float ; A one or two element array in normalized coordinates that gives the location of the ; plot title. The plot is centered on this location. If one element, the X location is taken ; as 0.5 and the element is used as the Y location. Othersize, use [x,y]. Default is [0.5,0.9]. ; tsize: in, optional, type=float ; The character size for the title. Normally, the title character size is 1.1 times ; the character size of the surface annotation. ; tspace: in, optional, type=float ; The title Y spacing. This should be a number, between 0 and 1 that is the fraction ; of the distance between !Y.Window[1] and !Y.Window[0] to locate the title above ; !Y.Window[1]. When Total(!P.MULTI) EQ 0, the default is 0.005, and it is 0.0025 otherwise. ; Now depreciated in favor of `TLocation`. ; window: in, optional, type=boolean, default=0 ; Set this keyword to replace all the commands in the current cgWindow or to ; create a new cgWindow, if one doesn't currenly exist, for displaying this command. ; To create a new cgWindow if one currenly exists, use the `cgWindow` command ; xstyle: in, hidden ; The normal XSTYLE keyword. ; xtitle: in, optional, type=string ; The X title of the plot. ; ystyle: in, hidden ; The normal YSTYLE keyword. ; ytitle: in, optional, type=string ; The Y title of the plot. ; zstyle: in, hidden ; The normal ZSTYLE keyword. ; ztitle: in, optional, type=string ; The Z title of the plot. ; _ref_extra: in, optional, type=any ; Any keyword appropriate for the `IDL Surface command ` ; is allowed in the program. ; ; :Examples: ; Use as you would use the IDL SURFACE of SHADE_SURF command:: ; data = Dist(200) ; LoadCT, 33 ; cgSurf, data ; cgSurf, data, Shades=BytScl(data) ; cgSurf, data, /Shaded ; cgSurf, data, /Shaded, Shades=BytScl(data) ; ; :Author: ; FANNING SOFTWARE CONSULTING:: ; David W. Fanning ; 1645 Sheely Drive ; Fort Collins, CO 80526 USA ; Phone: 970-221-0438 ; E-mail: david@idlcoyote.com ; Coyote's Guide to IDL Programming: http://www.idlcoyote.com ; ; :History: ; Change History:: ; Written, 13 November 2010. DWF. ; Now setting decomposition state by calling cgSetColorState. 16 November 2010. DWF. ; Added TSIZE and TSPACE keywords to treak title size and placement, ; as necessary. 17 November 2010. DWF. ; Background keyword now applies in PostScript file as well. 17 November 2010. DWF. ; Many changes after BACKGROUND changes to get !P.MULTI working again! 18 November 2010. DWF. ; Changes so that color variables don't change type. 23 Nov 2010. DWF. ; Added ELEVATION_SHADING keyword. 26 Nov 2010. DWF. ; I had keyword conflicts with the AX and AZ rotation keywords. Now perform rotation with ; ROTX and ROTZ keywords. 7 Dec 2010. DWF. ; Added WINDOW keyword to allow graphic to be displayed in a resizable graphics window. 8 Dec 2010. DWF ; Changed the Title size to 1.1 times the character size of the plot. 14 Dec 2010. DWF. ; Modifications to allow cgSurf to be drop-in replacement for old Surface commands in ; indexed color mode. 24 Dec 2010. DWF. ; Previous changes introduced problems with OVERPLOT that have now been fixed. 28 Dec 2010. DWF. ; Set NOERASE keyword from !P.NoErase system variable when appropriate. 28 Dec 2010. DWF. ; Additional problems with NOERASE discovered and solved. 29 Dec 2010. DWF. ; Change to DECOMPOSED color was using incorrect color tables. 29 Dec 2010. DWF. ; In some cases, I was turning BYTE values to strings without converting to ; INTEGERS first. 30 Dec 2010. DWF. ; Moved setting to decomposed color before color selection process to avoid PostScript ; background problems when passed 24-bit color integers. 12 Jan 2011. DWF. ; Fixed a problem in which I assumed the background color was a string. 18 Jan 2011. DWF. ; Added ADDCMD keyword. 26 Jan 2011. DWF. ; Added LAYOUT keyword. 28 Jan 2011. DWF. ; Added PALETTE keyword. 3 Feb 2011. DWF. ; Color table vectors must be obtained AFTER loading the color palette. 6 March 2011. DWF. ; Modified error handler to restore the entry decomposition state if there is an error. 17 March 2011. DWF ; Modified the color decomposition state to properly handle shaded surfaces in PostScript. 20 July 2011. DWF. ; Updated the BACKGROUND color selection from lessons learned in 27 Oct 2011 cgContour ; corrections. 27 Oct 2011. DWF. ; Added the ability to send the output directly to a file via the OUTPUT keyword. 9 Dec 2011, DWF. ; PostScript, PDF, and Imagemagick parameters can now be tailored with cgWindow_SetDefs. 14 Dec 2001. DWF. ; Now allowing the user to draw in the "background" color, if the COLOR or AXISCOLOR is "BACKGROUND". 19 March 2012. DWF. ; Added the ability to use escape characters in plot titles to specify cgSymbol symbols. 27 July 2012. DWF. ; Lost the XTitle and YTitle keywords when doing shaded surfaces. 15 December 2012. DWF. ; Still had some color issues with shaded surfaces having to be done in indexed color to sort out. ; This appears to work now both on the display and in PostScript. 25 Jan 2013. DWF. ; Added the TLOCATION keyword and depreciated the TSPACE keyword. 27 May 2013. DWF. ; Fixed a problem with shaded surfaces that set the top color to the background color. 12 June 2013. DWF. ; Fixed a problem with the TITLE keyword that was resetting !P.T, even if the SAVE keyword was set. 25 Sept 2013. DWF. ; ; :Copyright: ; Copyright (c) 2010-2013, Fanning Software Consulting, Inc. ;- PRO cgSurf, data, x, y, $ ADDCMD=addcmd, $ AXISCOLOR=saxiscolor, $ AXESCOLOR=saxescolor, $ BACKGROUND=sbackground, $ BOTTOM=sbottom, $ CHARSIZE=charsize, $ COLOR=scolor, $ ELEVATION_SHADING=elevation_shading, $ FONT=font, $ LAYOUT=layout, $ NOERASE=noerase, $ OUTFILENAME=outfilename, $ OUTPUT=output, $ PALETTE=palette, $ ROTX=rotx, $ ROTZ=rotz, $ SHADED=shaded, $ SHADES=shades, $ SKIRT=skirt, $ TITLE=title, $ TRADITIONAL=traditional, $ TLOCATION=tlocation, $ TSIZE=tsize, $ TSPACE=tspace, $ WINDOW=window, $ XSTYLE=xstyle, $ XTITLE=xtitle, $ YSTYLE=ystyle, $ YTITLE=ytitle, $ ZSTYLE=zstyle, $ ZTITLE=ztitle, $ _Ref_Extra=extra Compile_Opt idl2 Catch, theError IF theError NE 0 THEN BEGIN Catch, /CANCEL void = cgErrorMsg() IF N_Elements(thisMulti) NE 0 THEN !P.Multi = thisMulti IF N_Elements(currentState) NE 0 THEN cgSetColorState, currentState RETURN ENDIF ; Did user pass parameters? IF N_Elements(data) EQ 0 THEN BEGIN Print, 'USE SYNTAX: cgSurf, data, x, y, [SHADED=1]' RETURN ENDIF ; Set up PostScript device for working with colors. IF !D.Name EQ 'PS' THEN Device, COLOR=1, BITS_PER_PIXEL=8 ; Do they want this plot in a resizeable graphics window? IF Keyword_Set(addcmd) THEN window = 1 IF Keyword_Set(window) AND ((!D.Flags AND 256) NE 0) THEN BEGIN ; If you are using a layout, you can't ever erase. IF N_Elements(layout) NE 0 THEN noerase = 1 currentWindow = cgQuery(/CURRENT, COUNT=wincnt) IF wincnt EQ 0 THEN replaceCmd = 0 ELSE replaceCmd=1 ; If adding a command, have to do this differently. IF Keyword_Set(addcmd) THEN BEGIN cgWindow, 'cgSurf', data, x, y, $ AXISCOLOR=saxiscolor, $ AXESCOLOR=saxescolor, $ BACKGROUND=sbackground, $ BOTTOM=sbottom, $ CHARSIZE=charsize, $ COLOR=scolor, $ ELEVATION_SHADING=elevation_shading, $ FONT=font, $ LAYOUT=layout, $ NOERASE=noerase, $ PALETTE=palette, $ ROTX=rotx, $ ROTZ=rotz, $ SHADED=shaded, $ SHADES=shades, $ SKIRT=skirt, $ TITLE=title, $ TRADITIONAL=traditional, $ TLOCATION=tlocation, $ TSIZE=tsize, $ TSPACE=tspace, $ XSTYLE=xstyle, $ XTITLE=xtitle, $ YSTYLE=ystyle, $ YTITLE=ytitle, $ ZSTYLE=zstyle, $ ZTITLE=ztitle, $ ADDCMD=1, $ _Extra=extra RETURN ENDIF ; Otherwise, we are just replacing the commands in a new or existing window. cgWindow, 'cgSurf', data, x, y, $ AXISCOLOR=saxiscolor, $ AXESCOLOR=saxescolor, $ BACKGROUND=sbackground, $ BOTTOM=sbottom, $ CHARSIZE=charsize, $ COLOR=scolor, $ ELEVATION_SHADING=elevation_shading, $ FONT=font, $ LAYOUT=layout, $ NOERASE=noerase, $ PALETTE=palette, $ ROTX=rotx, $ ROTZ=rotz, $ SHADED=shaded, $ SHADES=shades, $ SKIRT=skirt, $ TITLE=title, $ TRADITIONAL=traditional, $ TLOCATION=tlocation, $ TSIZE=tsize, $ TSPACE=tspace, $ XSTYLE=xstyle, $ XTITLE=xtitle, $ YSTYLE=ystyle, $ YTITLE=ytitle, $ ZSTYLE=zstyle, $ ZTITLE=ztitle, $ REPLACECMD=replaceCmd, $ _Extra=extra RETURN ENDIF ; Are we doing some kind of output? IF (N_Elements(output) NE 0) && (output NE "") THEN BEGIN ; If the output string has a dot character, then this must be a ; filename, and we will determine the type of file from the filename extension. IF StrPos(output, '.') NE -1 THEN BEGIN root_name = cgRootName(output, DIRECTORY=theDir, EXTENSION=ext) IF theDir EQ "" THEN CD, CURRENT=theDir outfilename = output outputSelection = StrUpCase(ext) ENDIF IF N_Elements(outputSelection) EQ 0 THEN outputSelection = StrUpCase(output) typeOfOutput = ['PS','EPS','PDF','BMP','GIF','JPEG','JPG','PNG','TIFF', 'TIF'] void = Where(typeOfOutput EQ outputSelection, count) IF count EQ 0 THEN Message, 'Cannot find ' + outputSelection + ' in allowed output types.' ; Set things up. CASE outputSelection OF 'PS': BEGIN ext = '.ps' delete_ps = 0 END 'EPS': BEGIN ext = '.eps' encapsulated = 1 delete_ps = 0 END 'PDF': BEGIN ext = '.pdf' pdf_flag = 1 delete_ps = 1 END 'BMP': BEGIN ext = '.bmp' bmp_flag = 1 delete_ps = 1 END 'GIF': BEGIN ext = '.gif' gif_flag = 1 delete_ps = 1 END 'JPEG': BEGIN ext = '.jpg' jpeg_flag = 1 delete_ps = 1 END 'JPG': BEGIN ext = '.jpg' jpeg_flag = 1 delete_ps = 1 END 'PNG': BEGIN ext = '.png' png_flag = 1 delete_ps = 1 END 'TIFF': BEGIN ext = '.tif' tiff_flag = 1 delete_ps = 1 END 'TIF': BEGIN ext = '.tif' tiff_flag = 1 delete_ps = 1 END ENDCASE ; Do you need a filename? IF ( (N_Elements(outfilename) EQ 0) || (outfilename EQ "") ) THEN BEGIN filename = 'cgplot' + ext outfilename = cgPickfile(FILE=filename, TITLE='Select Output File Name...', $ FILTER=ext, /WRITE) IF outfilename EQ "" THEN RETURN ENDIF ; We need to know the root name of the file, because we have to make a PostScript ; file of the same name. At least we do if the type is not PS or EPS. IF (outputSelection NE 'PS') && (outputSelection NE 'EPS') THEN BEGIN root_name = cgRootName(outfilename, DIRECTORY=theDir) IF theDir EQ "" THEN CD, CURRENT=theDir ps_filename = Filepath(ROOT_DIR=theDir, root_name + '.ps') ENDIF ELSE ps_filename = outfilename ; Get the output default values. cgWindow_GetDefs, $ PS_Charsize = ps_charsize, $ ; The PostScript character size. PS_FONT = ps_font, $ ; Select the font for PostScript output. PS_Decomposed = ps_decomposed, $ ; Sets the PostScript color mode. PS_Delete = ps_delete, $ ; Delete PS file when making IM raster. PS_Metric = ps_metric, $ ; Select metric measurements in PostScript output. PS_Scale_factor = ps_scale_factor, $ ; Select the scale factor for PostScript output. PS_TT_Font = ps_tt_font ; Select the true-type font to use for PostScript output. ; Set up the PostScript device. cgPS_Open, $ CHARSIZE=ps_charsize, $ DECOMPOSED=ps_decomposed, $ FILENAME=ps_filename, $ FONT=ps_font , $ ENCAPSULATED=encapsulated, $ METRIC=ps_metric, $ SCALE_FACTOR=ps_scale_factor, $ TT_FONT=ps_tt_font, $ QUIET=1 ENDIF ; Going to draw in decomposed color, if possible to avoid dirtying the color table. cgSetColorState, 1, CURRENTSTATE=currentState ; If current state is "indexed color" and colors are represented as long integers then "fix" them. IF (currentState EQ 0) THEN BEGIN IF Size(sbackground, /TNAME) EQ 'LONG' THEN sbackground = Fix(sbackground) IF Size(scolor, /TNAME) EQ 'LONG' THEN scolor = Fix(scolor) ENDIF ; Pay attention to !P.Noerase in setting the NOERASE kewyord. This must be ; done BEFORE checking the LAYOUT properties. IF !P.NoErase NE 0 THEN noerase = !P.NoErase ELSE noerase = Keyword_Set(noerase) ; Set up the layout, if necessary. IF N_Elements(layout) NE 0 THEN BEGIN thisMulti = !P.Multi totalPlots = layout[0]*layout[1] !P.Multi = [0,layout[0], layout[1], 0, 0] IF layout[2] EQ 1 THEN BEGIN noerase = 1 !P.Multi[0] = 0 ENDIF ELSE BEGIN !P.Multi[0] = totalPlots - layout[2] + 1 ENDELSE ENDIF ; Check parameters. IF N_Elements(title) EQ 0 THEN title = "" ELSE title = cgCheckForSymbols(title) IF N_Elements(xtitle) EQ 0 THEN xtitle = "" ELSE xtitle = cgCheckForSymbols(xtitle) IF N_Elements(ytitle) EQ 0 THEN ytitle = "" ELSE ytitle = cgCheckForSymbols(ytitle) IF N_Elements(ztitle) EQ 0 THEN ztitle = "" ELSE ztitle = cgCheckForSymbols(ztitle) ndims = Size(data, /N_DIMENSIONS) IF ndims NE 2 THEN Message, 'Data must be 2D.' s = Size(data, /DIMENSIONS) IF N_Elements(x) EQ 0 THEN x = Findgen(s[0]) IF N_Elements(y) EQ 0 THEN y = Findgen(s[1]) noerase = Keyword_Set(noerase) ; Load a color palette, if you have one. IF N_Elements(palette) NE 0 THEN BEGIN IF Size(palette, /N_DIMENSIONS) NE 2 THEN Message, 'Color palette is not a 3xN array.' dims = Size(palette, /DIMENSIONS) threeIndex = Where(dims EQ 3) IF ((threeIndex)[0] LT 0) THEN Message, 'Color palette is not a 3xN array.' IF threeIndex[0] EQ 0 THEN palette = Transpose(palette) TVLCT, palette ENDIF ; Get the current color table vectors. TVLCT, rr, gg, bb, /GET ; Check the color keywords. background = cgDefaultColor(sbackground, /BACKGROUND, TRADITIONAL=traditional) IF (N_Elements(saxisColor) EQ 0) && (N_Elements(saxesColor) NE 0) THEN saxisColor = saxesColor axisColor = cgDefaultColor(saxisColor, TRADITIONAL=traditional) color = cgDefaultColor(sColor, DEFAULT='BLU6', TRADITIONAL=traditional) bottom = cgDefaultColor(sbottom, DEFAULT=color, TRADITIONAL=traditionale) ; If color is the same as background, do something. Since this precludes drawing the the ; background color (perhaps you want to "erase" something), I offer an exception. If the ; COLOR is "Background", I am going to assume you know what you are doing! IF ColorsAreIdentical(background, color) THEN BEGIN IF ((!D.Flags AND 256) NE 0) THEN BEGIN IF (!P.Multi[0] EQ 0) && (~Keyword_Set(overplot) && ~noerase) THEN cgErase, background ENDIF IF (Size(color, /TNAME) EQ 'STRING') THEN BEGIN IF (StrUpCase(color) NE 'BACKGROUND') THEN color = 'OPPOSITE' ENDIF ELSE color = 'OPPOSITE' ENDIF IF ColorsAreIdentical(background, axiscolor) THEN BEGIN IF ((!D.Flags AND 256) NE 0) THEN BEGIN IF (!P.Multi[0] EQ 0) && (~Keyword_Set(overplot) && ~noerase) THEN cgErase, background ENDIF IF (Size(axiscolor, /TNAME) EQ 'STRING') THEN BEGIN IF (StrUpCase(axiscolor) NE 'BACKGROUND') THEN axiscolor = 'OPPOSITE' ENDIF ELSE axiscolor = 'OPPOSITE' ENDIF elevation_shading = Keyword_Set(elevation_shading) ; Character size has to be determined *after* the layout has been decided. IF N_Elements(font) EQ 0 THEN IF (!D.Name EQ 'PS') THEN font = 1 ELSE font = !P.font IF N_Elements(charsize) EQ 0 THEN charsize = cgDefCharSize(FONT=font) * 1.25 ; Other properties. IF N_Elements(rotx) EQ 0 THEN rotx = 30 IF N_Elements(rotz) EQ 0 THEN rotz = 30 IF N_Elements(xstyle) EQ 0 THEN xstyle = 0 IF N_Elements(ystyle) EQ 0 THEN ystyle = 0 IF N_Elements(zstyle) EQ 0 THEN zstyle = 0 ; Load the drawing colors. If needed create a window first, so the drawing ; colors are correct for the window you want to draw into. IF ((!D.Flags AND 256) NE 0) && (!D.Window LT 0) THEN cgDisplay IF Size(axiscolor, /TNAME) EQ 'STRING' THEN BEGIN axiscolor = cgColor(axiscolor) ENDIF ELSE BEGIN IF currentState EQ 0 THEN axiscolor = cgColor24(rr[axiscolor], gg[axiscolor], bb[axiscolor]) ENDELSE IF Size(bottom, /TNAME) EQ 'STRING' THEN BEGIN bottom = cgColor(bottom) ENDIF ELSE BEGIN IF currentState EQ 0 THEN bottom = cgColor24(rr[bottom], gg[bottom], bb[bottom]) ENDELSE IF Size(color, /TNAME) EQ 'STRING' THEN BEGIN color = cgColor(color) ENDIF ELSE BEGIN IF currentState EQ 0 THEN color = cgColor24(rr[color], gg[color], bb[color]) ENDELSE IF Size(background, /TNAME) EQ 'STRING' THEN BEGIN originalbg = background background = cgColor(background) shadebackground = cgColor(originalbg, DECOMPOSED=0, 254) ENDIF ELSE BEGIN ; Different values based on current state of the device. Indexed color mode here. IF currentState EQ 0 THEN BEGIN originalbg = [rr[background], gg[background], bb[background]] background = cgColor24(rr[background], gg[background], bb[background]) ENDIF ; Decomposed color mode here. Not sure how this should be handled. Just ; do white. If it is not right, then use strings for color values! IF currentState EQ 1 THEN BEGIN orginalbg = 'white' shadebackground = cgColor('white', DECOMPOSED=0, 254) ENDIF ENDELSE ; Get the color table with loaded drawing colors. TVLCT, rl, gl, bl, /GET ; Are you doing elevation shading? IF elevation_shading THEN BEGIN IF N_Elements(shades) EQ 0 THEN shades = BytScl(data, /NAN) ENDIF ; Do you need a PostScript background color? Lot's of problems here! ; Basically, I MUST draw a plot to advance !P.MULTI. But, drawing a ; plot of any sort erases the background color. So, I have to draw a ; plot, store the new system variables, then draw my background, etc. ; I have tried LOTS of options. This is the only one that worked. IF !D.Name EQ 'PS' THEN BEGIN IF ~noerase THEN BEGIN ; I only have to do this, if this is the first plot. IF !P.MULTI[0] EQ 0 THEN BEGIN ; Save the current system variables. Will need to restore later. bangx = !X bangy = !Y bangz = !Z bangp = !P ; Make sure axis are turned off. I don't really want to draw anything, ; just advance !P.MULTI or "erase" the display for the next plot. IF cgBitGet(xstyle, 2) NE 1 THEN xxstyle = xstyle + 4 ELSE xxstyle = xstyle IF cgBitGet(ystyle, 2) NE 1 THEN yystyle = ystyle + 4 ELSE yystyle = ystyle IF cgBitGet(zstyle, 2) NE 1 THEN zzstyle = zstyle + 4 ELSE zzstyle = zstyle ; Draw the plot that doesn't draw anything. Surface, data, x, y, XSTYLE=xxstyle, YSTYLE=yystyle, ZSTYLE=zzstyle, $ CHARSIZE=charsize, SKIRT=skirt, _STRICT_EXTRA=extra, $ AX=rotx, AZ=rotz ; Save the "after plot" system variables. Will use later. afterx = !X aftery = !Y afterz = !Z afterp = !P ; Draw the background color and set the variables you will need later. PS_Background, background psnodraw = 1 tempNoErase = 1 ; Restore the original system variables so that it is as if you didn't ; draw the invisible plot. !X = bangx !Y = bangy !Z = bangz !P = bangp ENDIF ELSE tempNoErase = noerase ENDIF ELSE tempNoErase = noerase ENDIF ELSE tempNoErase = noerase bangx = !X bangy = !Y bangz = !Z bangp = !P ; Draw the surface axes, but no data. Surface, data, x, y, COLOR=axiscolor, BACKGROUND=background, BOTTOM=bottom, $ /NODATA, XSTYLE=xstyle, YSTYLE=ystyle, ZSTYLE=zstyle, $ FONT=font, CHARSIZE=charsize, NOERASE=tempNoErase, _STRICT_EXTRA=extra, $ AX=rotx, AZ=rotz, XTITLE=xtitle, YTITLE=ytitle, ZTITLE=ztitle ; Storing these system variable is *required* to make !P.MULTI work correct. ; Do not delete! newx = !X newy = !y newz = !Z newP = !P !X = bangx !Y = bangy !Z = bangz !P = bangp ; Turn the axes off to draw the surface itself. Start by making sure bit 4 in ; the [XYZ]Style bits are turned on. IF cgBitGet(xstyle, 2) NE 1 THEN xxstyle = xstyle + 4 ELSE xxstyle = xstyle IF cgBitGet(ystyle, 2) NE 1 THEN yystyle = ystyle + 4 ELSE yystyle = ystyle IF cgBitGet(zstyle, 2) NE 1 THEN zzstyle = zstyle + 4 ELSE zzstyle = zstyle ; Make absolutely sure the colors are fresh. IF N_Elements(palette) NE 0 THEN TVLCT, palette ELSE TVLCT, rr, gg, bb ; Draw either a wire mesh or shaded surface. Care has to be taken if ; the SHADES keyword is used, because this also has to be done in indexed ; color mode. IF Keyword_Set(shaded) THEN BEGIN ; We have to get the background color out of the surface color ; range to do this in a device independent way. Set_Shading, VALUES=[1,253] ; ; Depending upon the original background color, load the color ; ; in color table index 254. ; IF Size(originalbg, /TNAME) EQ 'STRING' $ ; THEN orignalbg = cgColor(originalBg, 254) $ ; ELSE TVLCT, Reform(origialbg), 254 ; Restrict the current color table vectors to the range 1-254. LoadCT, 0, /Silent IF N_Elements(palette) NE 0 THEN BEGIN TVLCT, Congrid(palette[*,0],253), Congrid(palette[*,1],253), Congrid(palette[*,2],253), 1 ENDIF ELSE BEGIN TVLCT, Congrid(rr,253), Congrid(gg,253), Congrid(bb,253), 1 ENDELSE ; If shades is defined, then we have to make sure the values there ; are in the range 1-254. IF N_Elements(shades) NE 0 THEN BEGIN IF Max(shades,/NAN) GT 253 $ THEN checkShades = BytScl(shades, TOP=252) + 1B $ ELSE checkShades = shades ENDIF ; All shaded surfaces have to be done in indexed color mode. cgSetColorState, 0 ; Shaded surface plot. Should be no axes here at all. Shade_Surf, data, x, y, /NOERASE, SHADES=checkShades, $ XSTYLE=xxstyle, YSTYLE=yystyle, ZSTYLE=zzstyle, _STRICT_EXTRA=extra, $ BACKGROUND=shadebackground, AX=rotx, AZ=rotz, CHARSIZE=charsize, $ XTITLE=xtitle, YTITLE=ytitle, ZTITLE=ztitle;;;;;, COLOR=color, BOTTOM=bottom cgSetColorState, 1 ; Have to repair the axes. Do this in decomposed color mode, if possible. TVLCT, rl, gl, bl ; Color table after colors are loaded. Surface, data, x, y, COLOR=axiscolor, BACKGROUND=background, BOTTOM=bottom, $ /NODATA, /NOERASE, XSTYLE=xstyle, YSTYLE=ystyle, ZSTYLE=zstyle, $ FONT=font, CHARSIZE=charsize, SKIRT=skirt, _STRICT_EXTRA=extra, AX=rotx, AZ=rotz, $ XTitle=xtitle, YTitle=ytitle ; Shading parameters are "sticky", but I can't tell what they ; were when I came into the program. Here I just set them back ; to their default values. Set_Shading, VALUES=[0,255] ENDIF ELSE BEGIN ; We can draw the surface in decomposed color mode, unless the SHADES ; keyword is being used. Then we have to use indexed color mode. IF N_Elements(shades) NE 0 THEN BEGIN cgSetColorState, 0 TVLCT, rl, gl, bl ; Color table after colors are loaded. Surface, data, x, y, NOERASE=1, SHADES=shades, $ XSTYLE=xxstyle, YSTYLE=yystyle, ZSTYLE=zzstyle, $ FONT=font, CHARSIZE=charsize, _STRICT_EXTRA=extra, AX=rotx, AZ=rotz , $ XTITLE=xtitle, YTITLE=ytitle, ZTITLE=ztitle cgSetColorState, 1 ENDIF ELSE BEGIN TVLCT, rl, gl, bl ; Color table after colors are loaded. Surface, data, x, y, NOERASE=1, COLOR=color, BOTTOM=bottom, $ BACKGROUND=background, SKIRT=skirt, $ XSTYLE=xxstyle, YSTYLE=yystyle, ZSTYLE=zzstyle, $ FONT=font, CHARSIZE=charsize, _STRICT_EXTRA=extra, AX=rotx, AZ=rotz , $ XTITLE=xtitle, YTITLE=ytitle, ZTITLE=ztitle ENDELSE ENDELSE ; If this is the first plot in PS, then we have to make it appear that we have ; drawn a plot, even though we haven't. IF N_Elements(psnodraw) EQ 1 THEN BEGIN newx = afterX newy = afterY newz = afterZ newp = afterP ENDIF ; Restore the decomposed color state to the input state. cgSetColorState, currentState ; Restore the color table. Can't do this for the Z-buffer or ; the snap shot will be incorrect. IF (!D.Name NE 'Z') THEN TVLCT, rr, gg, bb ; Update the system variables. !X = newx !Y = newy !Z = newz !P = newP ; Draw the title, if you have one. t3d = !P.T T3D, /RESET IF N_Elements(title) NE 0 THEN BEGIN cgSetColorState, 1, Current=currentState IF N_Elements(tsize) EQ 0 THEN BEGIN IF (!P.Charsize EQ 0) AND (N_Elements(charsize) EQ 0) THEN BEGIN titleSize = 1.10 ENDIF ELSE BEGIN IF (!P.Charsize NE 0) THEN titleSize = !P.Charsize * 1.10 IF (N_Elements(charsize) NE 0) THEN titleSize = charsize * 1.10 ENDELSE ENDIF ELSE titleSize = tsize IF (N_Elements(tlocation) EQ 0) && (N_Elements(tspace) NE 0) THEN BEGIN xloc = (!X.Window[1] - !X.Window[0]) / 2.0 + !X.Window[0] distance = !Y.Window[1] - !Y.Window[0] tspace = (Total(!P.Multi) EQ 0) ? 0.0025 : 0.00125 yloc = !Y.Window[1] + (distance * tspace) XYOutS, xloc, yloc, /NORMAL, ALIGNMENT=0.5, CHARSIZE=titleSize, $ title, FONT=font, COLOR=axiscolor ENDIF ELSE BEGIN IF N_Elements(tlocation) EQ 0 THEN tlocation = [0.5, 0.9] IF N_Elements(tlocation) EQ 1 THEN titleLocation = [0.5, tlocation] ELSE titleLocation = tlocation XYOutS, titleLocation[0], titleLocation[1], /NORMAL, ALIGNMENT=0.5, CHARSIZE=titleSize, $ title, FONT=font, COLOR=axiscolor ENDELSE cgSetColorState, currentState ENDIF !P.T = t3d ; Clean up if you are using a layout. IF N_Elements(layout) NE 0 THEN !P.Multi = thisMulti ; Are we producing output? If so, we need to clean up here. IF (N_Elements(output) NE 0) && (output NE "") THEN BEGIN ; Get the output default values. cgWindow_GetDefs, $ IM_Density = im_density, $ ; Sets the density parameter on ImageMagick convert command. IM_Options = im_options, $ ; Sets extra ImageMagick options on the ImageMagick convert command. IM_Resize = im_resize, $ ; Sets the resize parameter on ImageMagick convert command. IM_Transparent = im_transparent, $ ; Sets the "alpha" keyword on ImageMagick convert command. IM_Width = im_width, $ ; Sets the width of raster file output created with ImageMagick. PDF_Unix_Convert_Cmd = pdf_unix_convert_cmd, $ ; Command to convert PS to PDF. PDF_Path = pdf_path ; The path to the Ghostscript conversion command. ; Close the PostScript file and create whatever output is needed. cgPS_Close, DELETE_PS=delete_ps, $ ALLOW_TRANSPARENT=im_transparent, $ BMP=bmp_flag, $ DENSITY=im_density, $ GIF=gif_flag, $ GS_PATH=pdf_path, $ IM_OPTIONS=im_options, $ JPEG=jpeg_flag, $ PDF=pdf_flag, $ PNG=png_flag, $ RESIZE=im_resize, $ TIFF=tiff_flag, $ UNIX_CONVERT_CMD=pdf_unix_convert_cmd, $ WIDTH=im_width basename = File_Basename(outfilename) dirname = File_Dirname(outfilename) IF dirname EQ "." THEN CD, CURRENT=dirname Print, 'Output File: ' + Filepath(ROOT_DIR=dirname, basename) ENDIF END