; $Id: plot_ellipsebackup.pro,v 1.1 2004/07/22 15:13:47 dimeo Exp $
;+
; NAME:
;       FSC_SURFACE
;
; PURPOSE:
;
;       The purpose of this program is to demonstrate how to
;       create a rotating surface using object graphics.
;
; AUTHOR:
;
;       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
;
; CATEGORY:
;
;       Widgets, Object Graphics.
;
; CALLING SEQUENCE:
;
;       FSC_SURFACE, data, x, y
;
; REQUIRED INPUTS:
;
;       None. Fake data will be used if no data is supplied in call.
;
; OPTIONAL INPUTS
;
;       data: A 2D array of surface data.
;
;       x: A vector of X data values.
;
;       y: A vector of Y data values.
;
; OPTIONAL KEYWORD PARAMETERS:
;
;       BLOCK: Set this keyword to 1 to create a blocking widget program. This is
;       useful if you want to call this program from within a stopped program, for
;       example. Remember only the first blocking program actually blocks.
;
;       COLORTABLE: Set this keyword to a number between 0 and 40 to select one
;       of the pre-selected IDL color tables for elevation shading.
;
;       ELEVATION_SHADING: Set this keyword to put elevation shading into effect.
;
;       EXACT: Set this keyword to a one-, two-,or three-element array to set exact axis
;       scaling for the X, Y, and Z axes, respectively. If Exact is a one-element array,
;       all three axes are set to the same value. For example, to set the X axis to
;       exact scaling and the Y and Z axes to normal scaling, type:
;
;           IDL> FSC_Surface, Exact=[1,0,0]
;
;       _EXTRA: This keyword collects otherwise undefined keywords that are
;        passed to the IDLgrSURFACE initialization routine.
;
;       GROUP_LEADER: The group leader for this program. When the group leader
;       is destroyed, this program will be destroyed.
;
;       HIDDEN_LINES: Set this keyword to draw the surface with hidden lines removed.
;
;       LANDSCAPE: Set this keyword if you are printing in landscape mode. The
;       default is Portrait mode. The Landscape keyword on the PRINTER object
;       is set, but not all printers will honor this keyword setting. If yours
;       does not, set Landscape mode in the Printer Setup dialog.
;
;       POSITION: A two-, four- or six-element array of normalized (0 to 1) coordinates
;       used to position the X, Y, and Z axis in the coordinate space. Uses the form
;       [x0, x1, y0, y1, z0, z1]. In the absence of POSITION information, the Z position
;       is always [0,1] and the X and Y positions are calculated in a manner that
;       preserves the aspect ratio of the surface data.
;
;       SHADED: Set this keyword to set up a shaded surface plot rather than a wire
;       mesh surface, which is the default.
;
;       TITLE:  A string used as the title of the plot.
;
;       XTITLE: A string used as the X title of the plot.
;
;       YTITLE: A string used as the Y title of the plot.
;
;       ZTITLE: A string used as the Z title of the plot.


; COMMON BLOCKS:
;
;       None.
;
; SIDE EFFECTS:
;
;       None.
;
; DEPENDENCIES:
;
;       This program requires the following additional files from the Coyote Library:
;
;          error_message.pro
;          fsc_droplist.pro
;          getcolor.pro
;          loaddata.pro
;          pickcolor.pro
;          xcolors.pro
;
; EXAMPLE:
;
;       To use this program with your data, type:
;
;        IDL> FSC_Surface, data
;
;       Use your LEFT mouse button to rotate the surface plot in the window.
;       Use your RIGHT mouse button to zoom into a closer view of the plot.
;       Use your MIDDLE mouse button to zoom away from the plot.
;
; MODIFICATION HISTORY:
;
;       Written by David Fanning, 8 June 97.
;       Made axis scaling more robust. 17 Sept 97. DWF.
;       Minor modifications to incorporate better understanding
;          of how objects work. 4 Oct 97. DWF.
;       Fixed error cleaning up all of my created objects. 12 Feb 98. DWF.
;       Changed IDLgrContainer to IDL_Container to fix 5.1 problems. 20 May 98. DWF.
;       Fixed mis-spelling of HELVETICA14. 29 June 98. DWF.
;       Added the EXACT keyword to the X and Y axes to force axis ranging. 27 July 98. DWF
;       Added the ability to select rendering "drag" quality for faster operation. 22 Aug 98. DWF.
;       Added ability to get non-exact axis scaling. 12 May 99. DWF.
;       Improved documentation and readability of code. 12 May 99. DWF.
;       Added VECTOR and LANDSCAPE keywords and improved printing capability. 16 Feb 2000. DWF.
;       Added different lights and a Light Controller option. 28 April 2000. DWF.
;       Added elevation shading. 8 May 2000. DWF.
;       Removed VECTOR keyword. Replaced with VECTOR/BITMAP/COLOR Print buttons. 8 May 2000. DWF.
;       Added HIDDEN_LINE keyword. 8 May 2000. DWF.
;       Added EXACT keyword extensions and changed name from XSURFACE to FSC_SURFACE. 11 May 2000. DWF.
;       Made change to Light Control code to accomodate FSC_DROPLIST changes. 6 Jan 2001. DWF.
;       Removed unused color table vector code from a LONG time ago. 17 Jan 2001. DWF.
;       Added TIFF file output and removed GIF output for IDL 5.4. 11 Feb 2001. DWF.
;       Added short wait before taking image snapshot for JPEG and TIFF files to avoid
;          extraneous text in output files. 22 October 2001. DWF.
;       Added the POSITION keyword. 16 April 2002. DWF.
;       Added the ability to zoom into and out of plot with RIGHT and MIDDLE mouse buttons. 16 April 2002. DWF.
;       Fixed a problem with leaving lights on when switching to elevation shading. 16 April 2002. DWF.
;       Fixed a problem in not restoring viewport parameters after printing. 20 April 2002. DWF.
;       Added BMP, EPS, and PGN file output. 20 April 2002.DWF.
;       Added a BLOCK keyword. 11 May 2002. DWF.
;       Added a check for NAN in elevation colors part of the code. 14 August 2002. DWF.
;       Removed extra Save As TIFF File button. 24 August 2002. DWF.
;-
;
;###########################################################################
;
; LICENSE
;
; This software is OSI Certified Open Source Software.
; OSI Certified is a certification mark of the Open Source Initiative.
;
; Copyright © 1997-2002 Fanning Software Consulting.
;
; This software is provided "as-is", without any express or
; implied warranty. In no event will the authors be held liable
; for any damages arising from the use of this software.
;
; Permission is granted to anyone to use this software for any
; purpose, including commercial applications, and to alter it and
; redistribute it freely, subject to the following restrictions:
;
; 1. The origin of this software must not be misrepresented; you must
;    not claim you wrote the original software. If you use this software
;    in a product, an acknowledgment in the product documentation
;    would be appreciated, but is not required.
;
; 2. Altered source versions must be plainly marked as such, and must
;    not be misrepresented as being the original software.
;
; 3. This notice may not be removed or altered from any source distribution.
;
; For more information on Open Source Software, visit the Open Source
; web site: http://www.opensource.org.
;
;###########################################################################



FUNCTION Normalize, range, Position=position

    ; This is a utility routine to calculate the scaling vector
    ; required to position a vector of specified range at a
    ; specific position given in normalized coordinates. The
    ; scaling vector is given as a two-element array like this:
    ;
    ;   scalingVector = [translationFactor, scalingFactor]
    ;
    ; The scaling vector should be used with the [XYZ]COORD_CONV
    ; keywords of a graphics object or model. For example, if you
    ; wanted to scale an X axis into the data range of -0.5 to 0.5,
    ; you might type something like this:
    ;
    ;   xAxis->GetProperty, Range=xRange
    ;   xScale = Normalize(xRange, Position=[-0.5, 0.5])
    ;   xAxis, XCoord_Conv=xScale

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
;-------------------------------------------------------------------------



PRO CW_Light_Control_Intensity_Events, event

; Handles selection events from the Intensity Value widget.

   ; Get the info carrier.

parent = Widget_Info(event.id, /Parent)
infoCarrier = Widget_Info(parent, Find_by_UName='CW_LIGHT_CARRIER')
Widget_Control, infoCarrier, Get_UValue=info, /No_Copy

   ; Get the new intensity value.

info.theIntensity = *event.selection

   ; Change the intensity of the light.

info.theLight->SetProperty, Intensity=info.theIntensity

   ; Prepare to send an event that notifies the Simple_Surface program.

event_pro = info.event_pro
tlb = info.tlb
top = event.top
parent = info.parent
name = info.name
intensity = info.theIntensity
color = info.theColor
hide = info.theHide
Widget_Control, infoCarrier, Set_UValue=info, /No_Copy

IF event_pro NE "" THEN BEGIN
   eventStruct = {CW_LIGHT_CONTROL, ID:tlb, TOP:parent, HANDLER:0L, $
      NAME:name, INTENSITY:intensity, COLOR:color, HIDE:hide}
   Widget_Control, parent, Send_Event=eventStruct
ENDIF
END ;------------------------------------------------------------------------------



PRO CW_Light_Control_Events, event

   ; Get the info structure.

infoCarrier = Widget_Info(event.handler, Find_By_UName='CW_LIGHT_CARRIER')
Widget_Control, infoCarrier, Get_UValue=info, /No_Copy

   ; What kind of event is this? Branch appropriately.

Widget_Control, event.id, Get_UValue=thisEvent
CASE thisEvent OF

   'COLOR': BEGIN
      TVLCT, info.color, info.index
      DEVICE, Decomposed=0, Get_Decomposed=theDecomposedState
      thisColor = PickColor(Group_Leader=event.top, info.index)
      thisColor = Reform(thisColor, 3, 1)
      info.theLight->SetProperty, Color=thisColor
      info.theColor = thisColor
      DEVICE, Decomposed=theDecomposedState
      info.color = thisColor
      END

   'RESET': BEGIN
      info.theColor = info.origColor
      info.theIntensity = info.origIntensity
      info.theHide = info.origHide
      info.color = info.origColor
      info.intensityID->SetSelection, info.origIntensity
      IF info.origHide THEN BEGIN
         Widget_Control, info.onButtonID, Set_Button=0
         Widget_Control, info.offButtonID, Set_Button=1
      ENDIF ELSE BEGIN
         Widget_Control, info.onButtonID, Set_Button=1
         Widget_Control, info.offButtonID, Set_Button=0
      ENDELSE

      info.theLight->SetProperty, Intensity=info.origIntensity, $
         Color=info.origColor, Hide=info.origHide

      END

   'ON': BEGIN
      info.theHide = 0
      info.theLight->SetProperty, Hide=0
      END

   'OFF': BEGIN
      info.theHide = 1
      info.theLight->SetProperty, Hide=1
      END

ENDCASE

   ; Send an event if requested.

event_pro = info.event_pro
tlb = info.tlb
top = event.top
parent = info.parent
name = info.name
intensity = info.theIntensity
color = info.theColor
hide = info.theHide
Widget_Control, infoCarrier, Set_UValue=info, /No_Copy

IF event_pro NE "" THEN BEGIN
   eventStruct = {CW_LIGHT_CONTROL, ID:tlb, TOP:parent, HANDLER:0L, $
      NAME:name, INTENSITY:intensity, COLOR:color, HIDE:hide}
   Widget_Control, parent, Send_Event=eventStruct
ENDIF

END ;------------------------------------------------------------------------------



FUNCTION CW_Light_Control, parent, theLight, Name=name, UValue=uvalue, Event_Pro=event_pro, $
   LabelSize=labelsize, Index=index, Color=color

; This is a compound widget that allows one to manipulate various
; properties of light objects.

On_Error, 2

   ; Check parameters. Define defaults if necessary.

IF N_Elements(parent) EQ 0 THEN Message, 'Parent widget parameter is required 1st parameter.'
IF (N_Elements(theLight) EQ 0) OR (Size(theLight, /TName) NE 'OBJREF') THEN $
   Message, 'Light Object Reference is required 2nd parameter.'
IF N_Elements(uvalue) EQ 0 THEN uvalue = "LIGHT_CONTROL"
IF N_Elements(event_pro) EQ 0 THEN event_pro = ""
IF N_Elements(index) EQ 0 THEN index =  !D.Table_Size-2
IF N_Elements(color) EQ 0 THEN BEGIN
   TVLCT, r, g, b, /Get
   color = Reform([r[index], g[index], b[index]], 1, 3)
ENDIF ELSE color = Reform(color, 1, 3)
TVLCT, color, index

   ; Set the light properties.

theLight->GetProperty, Intensity=theIntensity, Hide=theHide, Color=theColor

IF N_Elements(name) EQ 0 THEN name = 'Light'

   ; Create the widgets.

tlb = Widget_Base(parent, Row=1, Base_Align_Center=1, $
   Event_Pro='CW_Light_Control_Events')
IF N_Elements(labelsize) NE 0 THEN $
   labelID = Widget_Label(tlb, Value=name + ': ', UNAME='CW_LIGHT_CARRIER', XSize=labelsize) ELSE $
   labelID = Widget_Label(tlb, Value=name + ': ', UNAME='CW_LIGHT_CARRIER')
exBaseID = Widget_Base(tlb, Row=1, /Exclusive, /Frame)
onButtonID = Widget_Button(exBaseID, Value='On', UValue='ON')
offButtonID = Widget_Button(exBaseID, Value='Off', UValue='OFF')
IF theHide THEN Widget_Control, offbuttonID, /Set_Button ELSE $
   Widget_Control, onbuttonID, /Set_Button
intensityValues = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
intensityID = FSC_Droplist(tlb, Title='Intensity:', Value = intensityValues, $
   Event_Pro='CW_Light_Control_Intensity_Events', Format='(F3.1)', Spaces=[1,1])
intensityID->SetSelection, theIntensity
colorID = Widget_Button(tlb, Value='Set Color', UValue='COLOR')
resetID= Widget_Button(tlb, Value='Reset', UValue='RESET')
Widget_Control, tlb, /Realize

   ; Create info structure with information to run the program. Store it.

info = {theLight:theLight, name:name, theIntensity:theIntensity, theHide:theHide, color:color, $
        theColor:theColor, Event_Pro:event_pro, origIntensity:theIntensity, index:index, $
        origColor:theColor, origHide:theHide, tlb:tlb, parent:parent, intensityID:intensityID, $
        onButtonID:onButtonID, offButtonID:offButtonID}
Widget_Control, labelID, Set_UValue=info, /No_Copy

RETURN, tlb
END ;------------------------------------------------------------------------------



PRO FSC_Surface_Elevation_Colors, event

; This event handler changes color tables for elevation shading.

Widget_Control, event.top, Get_UValue=info, /No_Copy

   ; What kind of event is this?

thisEvent = Tag_Names(event, /Structure_Name)
CASE thisEvent OF

   "WIDGET_BUTTON": BEGIN
      TVLCT, info.r, info.g, info.b
      XColors, Group_Leader=event.top, NotifyID=[event.id, event.top], $
         Title="FSC_Surface Elevation Shading Colors"
      ENDCASE

   "XCOLORS_LOAD": BEGIN
      info.r = event.r
      info.g = event.g
      info.b = event.b
      info.colortable = event.index
      IF Obj_Valid(info.thisPalette) THEN info.thisPalette->SetProperty, $
         Red=event.r, Green=event.g, Blue=event.b
      ENDCASE

ENDCASE

   ; Draw the graphic display.

info.thisWindow->Draw, info.thisView
Widget_Control, event.top, Set_UValue=info, /No_Copy
END ;------------------------------------------------------------------------------




PRO FSC_Surface_Elevation_Shading, event

; This event handler sets up elevation shading for the surface.

Widget_Control, event.top, Get_UValue=info, /No_Copy
Widget_Control, event.id, Get_Value=buttonValue, Get_UValue=uvalue
Widget_Control, event.id, Set_Value=uvalue, Set_UValue=buttonValue

CASE buttonValue OF
   'Elevation Shading ON': BEGIN
      s = Size(info.data, /Dimensions)
      info.thisSurface->SetProperty, Palette=info.thisPalette, $
         Vert_Colors=Reform(BytScl(info.data, /NAN), s[0]*s[1])

      sp = Size(info.data2, /Dimensions)
      info.thisSurface2->SetProperty, Palette=info.thisPalette, $
         Vert_Colors=Reform(BytScl(info.data2, /NAN), sp[0]*sp[1])
      Widget_Control, info.colorsID, Sensitive = 1

         ; Make sure lights are turned off.

      info.nonRotatingLight->SetProperty, Hide=1
      info.rotatingLight->SetProperty, Hide=1
      info.fillLight->SetProperty, Hide=1
      info.ambientLight->SetProperty, Hide=1
      Widget_Control, info.scolorID, Sensitive=0

      ENDCASE

   'Elevation Shading OFF': BEGIN
      info.thisSurface->SetProperty, Palette=Obj_New(), Vert_Colors=0
      info.thisSurface2->SetProperty, Palette=Obj_New(), Vert_Colors=0

      Widget_Control, info.colorsID, Sensitive = 0

         ; Make sure lights are turned on.

      info.nonRotatingLight->SetProperty, Hide=0
      info.rotatingLight->SetProperty, Hide=0
      info.fillLight->SetProperty, Hide=0
      info.ambientLight->SetProperty, Hide=0
      Widget_Control, info.scolorID, Sensitive=1

      ENDCASE

ENDCASE

   ; Draw the graphic display.

info.thisWindow->Draw, info.thisView
Widget_Control, event.top, Set_UValue=info, /No_Copy
END ;------------------------------------------------------------------------------



FUNCTION FSC_Surface_Aspect, aspectRatio, MARGIN=margin, WindowAspect=wAspectRatio

; This function calculates the correct aspect ratios for printing.

ON_ERROR, 2

   ; Check for aspect ratio parameter and possibilities.

IF N_PARAMS() EQ 0 THEN aspectRatio = 1.0

IF aspectRatio EQ 0 THEN BEGIN
   MESSAGE, 'Aspect Ratio of 0. Changing to 1...', /Informational
   aspectRatio = 1.0
ENDIF

s = SIZE(aspectRatio)
IF s(s(0)+1) NE 4 THEN $
   MESSAGE, 'Aspect Ratio is not a FLOAT. Take care...', /Informational

   ; Check for margins.

IF N_ELEMENTS(margin) EQ 0 THEN margin = 0.15

   ; Error checking.

IF margin LT 0 OR margin GE 0.5 THEN $
   MESSAGE, 'The MARGIN keyword value must be between 0.0 and 0.5.'

   ; Calculate the aspect ratio of the current window.

IF N_Elements(wAspectRatio) EQ 0 THEN wAspectRatio = FLOAT(!D.Y_VSIZE) / !D.X_VSIZE

   ; Calculate normalized positions in window.

IF (aspectRatio LE wAspectRatio) THEN BEGIN
   xstart = margin
   ystart = 0.5 - (0.5 - margin) * (aspectRatio / wAspectRatio)
   xend = 1.0 - margin
   yend = 0.5 + (0.5 - margin) * (aspectRatio / wAspectRatio)
ENDIF ELSE BEGIN
   xstart = 0.5 - (0.5 - margin) * (wAspectRatio / aspectRatio)
   ystart = margin
   xend = 0.5 + (0.5 - margin) * (wAspectRatio / aspectRatio)
   yend = 1.0 - margin
ENDELSE

position = [xstart, ystart, xend, yend]

RETURN, position
END
;-------------------------------------------------------------------------



Pro FSC_Surface_Cleanup, tlb

    ; Come here when program dies. Free all created objects.

Widget_Control, tlb, Get_UValue=info
IF N_Elements(info) NE 0 THEN Obj_Destroy, info.thisContainer
END
;-------------------------------------------------------------------



PRO FSC_Surface_Draw_Events, event

     ; Draw widget events handled here: expose events and trackball
     ; events. The trackball uses RSI-supplied TRACKBALL_DEFINE.PRO
     ; from the IDL50/examples/object directory.

Widget_Control, event.top, Get_UValue=info, /No_Copy
;help, info, /struct
drawTypes = ['PRESS', 'RELEASE', 'MOTION', 'SCROLL', 'EXPOSE']
thisEvent = drawTypes(event.type)

CASE thisEvent OF

   'EXPOSE':  ; Nothing required except to draw the view.
   'PRESS': BEGIN

         ; Zoom out on middle, zoom in on right, rotate on left.

       possibleButtons = ['NONE', 'LEFT', 'MIDDLE', 'NONE', 'RIGHT']
       thisButton = possibleButtons(event.press)

       CASE thisButton OF

         'RIGHT': BEGIN
                  info.thisView->GetProperty, Viewplane_Rect=thisRect
                  thisRect(0) = (thisRect(0) + 0.05) < thisRect(2)
                  thisRect(1) = (thisRect(1) + 0.05) < thisRect(3)
                  thisRect(2) = (thisRect(2) - 0.1) > thisRect(0)
                  thisRect(3) = (thisRect(3) - 0.1) > thisRect(1)
                  info.thisView->SetProperty, Viewplane_Rect=thisRect
                  END

         'MIDDLE': BEGIN
                  info.thisView->GetProperty, Viewplane_Rect=thisRect
                  thisRect(0) = thisRect(0) - 0.05
                  thisRect(1) = thisRect(1) - 0.05
                  thisRect(2) = thisRect(2) + 0.1
                  thisRect(3) = thisRect(3) + 0.1
                  info.thisView->SetProperty, Viewplane_Rect=thisRect
                  END

         'LEFT':  BEGIN
                  Widget_Control, event.id, Draw_Motion_Events=1 ; Motion events ON.
                  info.thisWindow->SetProperty, Quality=info.dragQuality ; Set Drag Quality.
                  END

         ELSE:
       ENDCASE

       END
   'RELEASE': BEGIN
       Widget_Control, event.id, Draw_Motion_Events=0 ; Motion events OFF.
       info.thisWindow->SetProperty, Quality=2 ; Drag Quality to High.
       END
   'MOTION': BEGIN ; Trackball events
       END
   ELSE:

ENDCASE

   ; Does the trackball need updating? If so, update.

needUpdate = info.thisTrackball->Update(event, Transform=thisTransform)
IF needUpdate THEN BEGIN
   info.thisModel->GetProperty, Transform=modelTransform
   info.thisModel->SetProperty, Transform=modelTransform # thisTransform
ENDIF

    ; Draw the view.

info.thisWindow->Draw, info.thisView

    ;Put the info structure back.

Widget_Control, event.top, Set_UValue=info, /No_Copy
END
;-------------------------------------------------------------------



PRO FSC_Surface_Style, event

     ; Event handler to select surface style.

Widget_Control, event.top, Get_UValue=info, /No_Copy

   ; Make sure lights are turned on.

info.nonRotatingLight->SetProperty, Hide=0
info.rotatingLight->SetProperty, Hide=0
info.fillLight->SetProperty, Hide=0
info.ambientLight->SetProperty, Hide=0
info.thisSurface->SetProperty, Color=info.surfColor
info.thisSurface2->SetProperty, Color=info.surfColor

    ; What style is wanted?

Widget_Control, event.id, Get_UValue=newStyle
CASE newStyle OF

   'DOTS': BEGIN
    info.thisSurface->SetProperty, Style=0
    info.thisSurface2->SetProperty, Style=0
    ENDCASE
   'MESH': BEGIN
    info.thisSurface->SetProperty, Style=1
    info.thisSurface2->SetProperty, Style=1
    ENDCASE
   'SOLID': BEGIN
    info.thisSurface->SetProperty, Style=2, Shading=1
    info.thisSurface2->SetProperty, Style=2, Shading=1
    ENDCASE
   'XPARALLEL': BEGIN
    info.thisSurface->SetProperty, Style=3
    info.thisSurface2->SetProperty, Style=3
    ENDCASE
   'YPARALLEL': BEGIN
    info.thisSurface->SetProperty, Style=4
    info.thisSurface2->SetProperty, Style=4
    ENDCASE
   'HIDDEN': BEGIN
       Widget_Control, event.id, Get_Value=buttonValue
       IF buttonValue EQ 'Hidden Lines OFF' THEN BEGIN
          setting = 0
          hlvalue = 'Hidden Lines ON'
       ENDIF ELSE BEGIN
          setting = 1
          hlvalue = 'Hidden Lines OFF'
       ENDELSE
       Widget_Control, event.id, Set_Value=hlvalue
       info.thisSurface->SetProperty, Hidden_Lines=setting
       info.thisSurface2->SetProperty, Hidden_Lines=setting
       ENDCASE

ENDCASE

    ; Redraw the graphic.

info.thisWindow->Draw, info.thisView

    ;Put the info structure back.

Widget_Control, event.top, Set_UValue=info, /No_Copy
END
;-------------------------------------------------------------------



PRO FSC_Surface_Properties, event

     ; Event handler to set graphic properties.

Widget_Control, event.top, Get_UValue=info, /No_Copy

    ; What property is wanted?

Widget_Control, event.id, Get_UValue=newProperty
CASE newProperty OF

   'ORIGINAL_T3D': info.thisModel->SetProperty, Transform=info.origTransform

       ; Surface color.

   'SURFACE_COLOR': BEGIN
      DEVICE, Decomposed=0, Get_Decomposed=theDecomposedState
      TVLCT, Reform(info.surfColor, 1, 3), info.surfIndex
      thisColor = PickColor(Group_Leader=event.top, info.surfIndex, $
         Cancel=cancelled, Title='Pick Surface Color...')
      IF NOT cancelled THEN BEGIn
         thisColor = Reform(thisColor, 3, 1)
         info.thisSurface->SetProperty, Color=thisColor
         info.thisSurface2->SetProperty, Color=thisColor
         info.surfColor = thisColor
      ENDIF
      DEVICE, Decomposed=theDecomposedState
      END

       ; Background color.

   'BBLACK': info.thisView->SetProperty, Color=[0,0,0]
   'BWHITE': info.thisView->SetProperty, Color=[255,255,255]
   'BCHARCOAL': info.thisView->SetProperty, Color=[80,80,80]
   'BGRAY': info.thisView->SetProperty, Color=[135, 135, 135]

       ; Axes colors.

   'ABLACK': BEGIN
      info.xAxis->SetProperty, Color=[0,0,0]
      info.yAxis->SetProperty, Color=[0,0,0]
      info.zAxis->SetProperty, Color=[0,0,0]
      END
   'AWHITE': BEGIN
      info.xAxis->SetProperty, Color=[255,255,255]
      info.yAxis->SetProperty, Color=[255,255,255]
      info.zAxis->SetProperty, Color=[255,255,255]
      END
   'AGREEN': BEGIN
      info.xAxis->SetProperty, Color=[0,255,0]
      info.yAxis->SetProperty, Color=[0,255,0]
      info.zAxis->SetProperty, Color=[0,255,0]
      END
   'AYELLOW': BEGIN
      info.xAxis->SetProperty, Color=[255,255,0]
      info.yAxis->SetProperty, Color=[255,255,0]
      info.zAxis->SetProperty, Color=[255,255,0]
      END
   'ANAVY': BEGIN
      info.xAxis->SetProperty, Color=[0, 0, 115]
      info.yAxis->SetProperty, Color=[0, 0, 115]
      info.zAxis->SetProperty, Color=[0, 0, 115]
      END

       ; Title colors.

   'TBLACK': info.plotTitle->SetProperty, Color=[0,0,0]
   'TWHITE': info.plotTitle->SetProperty, Color=[255,255,255]
   'TGREEN': info.plotTitle->SetProperty, Color=[0,255,0]
   'TYELLOW': info.plotTitle->SetProperty, Color=[255,255,0]
   'TNAVY': info.plotTitle->SetProperty, Color=[0,0,115]

      ; Color schemes.

   'B/W': BEGIN
      info.thisView->SetProperty, Color=[255,255,255]
      info.thisSurface->SetProperty, Color=[0,0,0]
      info.thisSurface2->SetProperty, Color=[0,0,0]
      info.surfColor = [0,0,0]
      info.xAxis->SetProperty, Color=[0,0,0]
      info.yAxis->SetProperty, Color=[0,0,0]
      info.zAxis->SetProperty, Color=[0,0,0]
      info.plotTitle->SetProperty, Color=[0,0,0]
      END
   'W/B': BEGIN
      info.thisView->SetProperty, Color=[0,0,0]
      info.thisSurface->SetProperty, Color=[255,255,255]
      info.thisSurface2->SetProperty, Color=[255,255,255]
      info.surfColor = [255,255,255]
      info.xAxis->SetProperty, Color=[255,255,255]
      info.yAxis->SetProperty, Color=[255,255,255]
      info.zAxis->SetProperty, Color=[255,255,255]
      info.plotTitle->SetProperty, Color=[255,255,255]
      END
   'ORIGINAL_COLORS': BEGIN
      info.thisView->SetProperty, Color=[80,80,80]
      info.thisSurface->SetProperty, Color=[255,255,255]
      info.thisSurface2->SetProperty, Color=[255,255,255]
      info.surfColor = [255,255,0]
      info.xAxis->SetProperty, Color=[0,255,0]
      info.yAxis->SetProperty, Color=[0,255,0]
      info.zAxis->SetProperty, Color=[0,255,0]
      info.plotTitle->SetProperty, Color=[0,255,0]
      END

   'DRAG_LOW': BEGIN
      info.dragQuality = 0
      Widget_Control, info.dragLowID, Sensitive=0
      Widget_Control, info.dragMedID, Sensitive=1
      Widget_Control, info.dragHighID, Sensitive=1
      END

   'DRAG_MEDIUM': BEGIN
      info.dragQuality = 1
      Widget_Control, info.dragMedID, Sensitive=0
      Widget_Control, info.dragLowID, Sensitive=1
      Widget_Control, info.dragHighID, Sensitive=1
      END

   'DRAG_HIGH': BEGIN
      info.dragQuality = 2
      Widget_Control, info.dragMedID, Sensitive=1
      Widget_Control, info.dragLowID, Sensitive=1
      Widget_Control, info.dragHighID, Sensitive=0
      END

ENDCASE

    ; Redraw the graphic.

info.thisWindow->Draw, info.thisView

    ;Put the info structure back.

Widget_Control, event.top, Set_UValue=info, /No_Copy
END
;-------------------------------------------------------------------



PRO FSC_Surface_Output, event

   ; This event handler creates GIF and JPEG files.

Widget_Control, event.top, Get_UValue=info, /No_Copy

   ; Get a snapshop of window contents. (TVRD equivalent.)
   ; Wait for just a moment to allow the pull-down menu button
   ; to disappear. Otherwise, we will get extraneous text in our
   ; output image.

Widget_Control, /Hourglass
Wait, 0.5
info.thisWindow->GetProperty, Image_Data=snapshot

   ; What kind of file is wanted?

Widget_Control, event.id, GET_UValue=whichFileType
CASE whichFileType OF

   'GIF': BEGIN

         ; Because we are using RGB color for this model, we have
         ; a 3-m-n array. Use Color_Quan to create a 2D image and
         ; appropriate color tables for the GIF file.

      image2D = Color_Quan(snapshot, 1, r, g, b)
      filename = Dialog_Pickfile(/Write, File='fsc_surface.gif')
      IF filename NE '' THEN Write_GIF, filename, image2d, r, g, b
      END

   'JPEG': BEGIN

      filename = Dialog_Pickfile(/Write, File='fsc_surface.jpg')
      IF filename NE '' THEN Write_JPEG, filename, snapshot, True=1, Quality=100
      END


   'TIFF': BEGIN

      filename = Dialog_Pickfile(/Write, File='fsc_surface.tif')
      IF filename NE '' THEN BEGIN

         ; TIFF files should have their Y direction reversed for
         ; compatibility with most other software.

         Write_TIFF, filename, Reverse(snapshot,3)
      ENDIF
      END

   'BMP': BEGIN
      filename = Dialog_Pickfile(/Write, File='fsc_surface.bmp')
      IF filename NE '' THEN Write_BMP, filename, snapshot
      END

   'PNG': BEGIN
      filename = Dialog_Pickfile(/Write, File='fsc_surface.png')
      IF filename NE '' THEN Write_PNG, filename, snapshot
      END

   'EPS': BEGIN
      filename = Dialog_Pickfile(/Write, File='fsc_surface.eps')
      IF filename NE '' THEN BEGIN
         info.thisWindow->GetProperty, Dimensions=viewDimensions, Units=viewUnits
         clipboard = Obj_New('IDLgrClipboard', Dimensions=viewDimensions, Unit=viewUnits)
         clipboard->Draw, info.thisView, /Postscript, Filename=filename
         Obj_Destroy, clipboard
      ENDIF
      END

ENDCASE

    ;Put the info structure back.

Widget_Control, event.top, Set_UValue=info, /No_Copy
END
;------------------------------------------------------------------------


PRO FSC_Surface_Exit, event

   ; Exit the program. This will cause the CLEANUP
   ; routine to be called automatically.

Widget_Control, event.top, /Destroy
END
;------------------------------------------------------------------------



PRO FSC_Surface_Light_Done, event
Widget_Control, event.top, /Destroy
END ;--------------------------------------------------------------------



PRO FSC_Surface_Light_Controls_Event, event
Widget_Control, event.top, Get_UValue=info
info.theWindow->Draw, info.theView
END
;-------------------------------------------------------------------------



PRO FSC_Surface_Light_Controls, event

   ; Place the light control beside the current widget program.

Widget_Control, event.top, Get_UValue=info, /No_Copy
Widget_Control, event.top, TLB_Get_Size=sizes, TLB_Get_Offset=offsets
xpos = sizes[0] + offsets[0] + 10
ypos = offsets[1] + 100

   ; Lights only make sense with a solid surface.

info.thisSurface->SetProperty, Style=2, Shading=1
info.thisSurface2->SetProperty, Style=2, Shading=1
info.thisWindow->Draw, info.thisView

   ; Create widgets.

tlb = Widget_Base(Title='FSC_Surface Light Controls', Column=1, Group_Leader=event.top, $
   UValue={theView:info.thisView, theWindow:info.thisWindow}, XOffset=xpos, YOffset=ypos)
dummy = CW_Light_Control(tlb, Name='Non-Rotating Light', info.nonRotatingLight, LabelSize=130, $
   Event_Pro='FSC_Surface_Light_Controls_Event', Index=!D.Table_Size-18, Color=[255,255,255])
dummy = CW_Light_Control(tlb, Name='Rotating Light', info.rotatingLight, LabelSize=130, $
   Event_Pro='FSC_Surface_Light_Controls_Event', Index=!D.Table_Size-19, Color=[255,255,255])
dummy = CW_Light_Control(tlb, Name='Fill Light', info.fillLight, LabelSize=130, $
   Event_Pro='FSC_Surface_Light_Controls_Event', Index=!D.Table_Size-20, Color=[255,255,255])
dummy = CW_Light_Control(tlb, Name='Ambient Light', info.ambientLight, LabelSize=130, $
   Event_Pro='FSC_Surface_Light_Controls_Event', Index=!D.Table_Size-21, Color=[255,255,255])
quit = Widget_Button(tlb, Value='Done', Event_Pro='FSC_Surface_Light_Done')

Widget_Control, tlb, /Realize

XManager, 'FSC_Surface_Light_Controls', tlb, /No_Block, Event_Handler='FSC_Surface_Light_Controls_Event'
Widget_Control, event.top, Set_UValue=info, /No_Copy

END
;-------------------------------------------------------------------------



PRO FSC_Surface_Printing, event

   ; Printer output handled here.

Widget_Control, event.top, Get_UValue=info, /No_Copy

   ; What kind of printing?

Widget_Control, event.id, Get_UValue=printType

   ; Does the user really want to print?

print_it = Dialog_PrinterSetup(info.thisPrinter)
IF NOT print_it THEN BEGIN
   Widget_Control, event.top, Set_UValue=info, /No_Copy
   RETURN
ENDIF

   ; Get the current viewport parameters.

info.thisView->GetProperty, Dimensions=viewDimensions, $
   Location=viewLocation, Units=viewUnits


IF printType NE 'COLOR' THEN BEGIN

   ; Find out the current colors of all the objects.

   info.thisView->GetProperty, Color=backgroundColor
   info.thisSurface->GetProperty, Color=surfaceColor
   info.thisSurface2->GetProperty, Color=surfaceColor
   info.xAxis->GetProperty, Color=axisColor
   info.yAxis->GetProperty, Color=axisColor
   info.zAxis->GetProperty, Color=axisColor
   info.thisPalette->GetProperty, Red=r, Green=g, Blue=b

   ; Change colors to black and white for printing.

   info.thisView->SetProperty, Color=[255,255,255]
   info.thisSurface->SetProperty, Color=[0,0,0]
   info.thisSurface2->SetProperty, Color=[0,0,0]
   info.xAxis->SetProperty, Color=[0,0,0]
   info.yAxis->SetProperty, Color=[0,0,0]
   info.zAxis->SetProperty, Color=[0,0,0]
   info.plotTitle->SetProperty, Color=[0,0,0]
   info.thisPalette->LoadCT, 0

ENDIF

   ; I want the output on the page to have the same aspect ratio
   ; (ratio of height to width) as I see in the display window.
   ; I use the FSC_Surface_Aspect function to calculate the
   ; correct viewport position in normalized coordinates. The
   ; return value of FSC_Surface_Aspect is the position of the
   ; viewport on the output page.

info.thisWindow->GetProperty, Dimensions=wdims
info.thisPrinter->GetProperty, Dimensions=pdims
plotAspect = Float(wdims[1]) / wdims[0]
printerAspect = Float(pdims[1]) / pdims[0]
position = FSC_Surface_Aspect(plotAspect, WindowAspect=printerAspect, Margin=0)

   ; Change the dimensions and postion of the viewport on the output page.
   ; Be sure to use normalized coordinates (units=3).

info.thisView->SetProperty, Dimensions=[position[2]-position[0], position[3]-position[1]], $
   Location=[position[0], position[1]], Units=3

   ; Print it. May take a little time. Alert the user.

Widget_Control, Hourglass=1
IF printType EQ 'VECTOR' THEN BEGIN
   info.thisPrinter->Draw, info.thisView, Vector=1
ENDIF ELSE BEGIN
   info.thisPrinter->Draw, info.thisView
ENDELSE
info.thisPrinter->NewDocument
Widget_Control, Hourglass=0

IF printType NE 'COLOR' THEN BEGIN

   ; Put everything back the way it was.

   info.thisView->SetProperty, Color=backgroundColor, Dimensions=[0,0], Location=[0,0]
   info.thisSurface->SetProperty, Color=surfaceColor
   info.thisSurface2->SetProperty, Color=surfaceColor
   info.xAxis->SetProperty, Color=axisColor
   info.yAxis->SetProperty, Color=axisColor
   info.zAxis->SetProperty, Color=axisColor
   info.plotTitle->SetProperty, Color=axisColor
   info.thisPalette->SetProperty, Red=r, Green=g, Blue=b

ENDIF

   ; Restore the viewport parameters.

info.thisView->SetProperty, Dimensions=viewDimensions, $
   Location=viewLocation, Units=viewUnits

   ; Put the info structure back.

Widget_Control, event.top, Set_UValue=info, /No_Copy
END
;-------------------------------------------------------------------



PRO FSC_Surface_Resize, event

     ; The only events generated by this simple program are resize
     ; events, which are handled here.

     ; Get the info structure.

Widget_Control, event.top, Get_UValue=info, /No_Copy

    ; Resize the draw widget. This is the proper way to do this
    ; in object graphics, but it does not always work in UNIX
    ; versions of IDL. If it doesn't work for you, comment the
    ; first line out and try the second. The second line is more
    ; portable, but not exactly the proper "object" way. :-(

info.thisWindow->SetProperty, Dimension=[event.x, event.y]
;Widget_Control, info.drawID, Draw_XSize=event.x, Draw_YSize=event.y

    ; Redisplay the graphic.

info.thisWindow->Draw, info.thisView

    ; Update the trackball objects location in the center of the
    ; window.

info.thisTrackball->Reset, [event.x/2, event.y/2], $
    (event.y/2) < (event.x/2)

    ;Put the info structure back.

Widget_Control, event.top, Set_UValue=info, /No_Copy
END
;-------------------------------------------------------------------



PRO plot_ellipse, rx,ry,rz,transmatrix, _Extra=extra, XTitle=xtitle, $
   YTitle=ytitle, ZTitle=ztitle, Title=plotTitle, Block=block, $
   Group_Leader=groupLeader, Exact=exact, Position=pos, $
   Landscape=landscape, Elevation_Shading=elevation, $
   Colortable=colortable, Shaded=shaded, Hidden_Lines=hidden_lines

   ; New printer functionality requires IDL 5.3 or higher.

IF Float(!Version.Release) LT 5.3 THEN BEGIN
   ok = Dialog_Message('Program functionality requires IDL 5.3 or higher. Returning...')
   RETURN
ENDIF

    ; Check for keywords.

;IF N_Elements(xtitle) EQ 0 THEN xtitle='X Axis'
;IF N_Elements(ytitle) EQ 0 THEN ytitle='Y Axis'
;IF N_Elements(ztitle) EQ 0 THEN ztitle='Z Axis'
IF N_Elements(xtitle) EQ 0 THEN xtitle='QX  x 100'
IF N_Elements(ytitle) EQ 0 THEN ytitle='QY x 100'
IF N_Elements(ztitle) EQ 0 THEN ztitle='Energy Transfer x 10'
IF N_Elements(plotTitle) EQ 0 THEN plotTitle='Resolution Ellipsoid'
IF N_Elements(colortable) EQ 0 THEN colortable = 4 ELSE colortable = 0 > colortable < 40
hidden_lines = Keyword_Set(hidden_lines)
elevation = Keyword_Set(elevation)
landscape = Keyword_Set(landscape)
IF Keyword_Set(shaded) THEN BEGIN
   shading = 1
   style = 2
ENDIF ELSE BEGIN
   shading = 0
   style = 1
ENDELSE
CASE N_Elements(exact) OF
   0: exact = [0,0,0]
   1: exact = Replicate(exact, 3)
   2: exact = [exact, 0]
   3:
   ELSE: BEGIN
      ok = Dialog_Message('Exact keyword contains too many elements. Returning...')
      RETURN
      ENDCASE
ENDCASE

    ; Need some data.

IF N_Elements(pos) EQ 0 THEN BEGIN

      ; I want the surface data to have the same aspect ratio as the data itself
      ; in the X and Y directions.

   ;surfaceAspect = Float(s[2]) / s[1]
   surfaceAspect = 1.0
   windowAspect = 1.0
   pos = FSC_Surface_Aspect(surfaceAspect, WindowAspect=windowAspect, Margin=0)
   pos = [pos[0], pos[2], pos[1], pos[3], 0.0, 1.0] - 0.5

ENDIF ELSE BEGIN

   CASE N_Elements(pos) OF

      2: BEGIN
         pos = [pos, 0.0, 1.0, 0.0, 1.0]
         pos[0] = 0.0 > pos[0]
         pos[1] = pos[1] < 1.0
         END

      4: BEGIN
         pos = [pos, 0.0, 1.0]
         pos[0] = 0.0 > pos[0]
         pos[1] = pos[1] < 1.0
         pos[2] = 0.0 > pos[2]
         pos[3] = pos[3] > 1.0
         END

      6: BEGIN
         pos[0] = 0.0 > pos[0]
         pos[1] = pos[1] < 1.0
         pos[2] = 0.0 > pos[2]
         pos[3] = pos[3] > 1.0
         pos[4] = 0.0 > pos[4]
         pos[5] = pos[5] > 1.0
         END

      ELSE: BEGIN
         ok = Dialog_Message('POSITION keyword must be a 2, 4, or 6 element array. Returning...')
         RETURN
         END

   ENDCASE

   pos = pos - 0.5

ENDELSE

TVLCT, r, g, b, /Get



   ; Calculate or use the position coordinates.





    ; Create a view. Use RGB color. Charcoal background.
    ; The coodinate system is chosen so that (0,0,0) is in the
    ; center of the window. This will make rotations easier.

thisView = OBJ_NEW('IDLgrView', Color=[80,80,80], $
   Viewplane_Rect=[-1.2,-1.1,2.3,2.3])
    ; Create a model for the surface and axes and add it to the view.
    ; This model will rotate under the direction of the trackball object.

thisModel = OBJ_NEW('IDLgrModel')
thisView->Add, thisModel

    ; Create a separate model for the title that doesn't rotate.

textModel = Obj_New('IDLgrModel')
thisView->Add, textModel

    ; Create helper objects. First, create title objects
    ; for the axes and plot. Color them green.

xTitle = Obj_New('IDLgrText', xtitle, Color=[0,255,0])
yTitle = Obj_New('IDLgrText', ytitle, Color=[0,255,0])
zTitle = Obj_New('IDLgrText', ztitle, Color=[0,255,0])

    ; Create font objects.

helvetica10pt = Obj_New('IDLgrFont', 'Helvetica', Size=10)
helvetica14pt = Obj_New('IDLgrFont', 'Helvetica', Size=14)

    ; Create a plot title object. I am going to place the title
    ; centered in X and towards the top of the viewplane rectangle.

plotTitle = Obj_New('IDLgrText', plotTitle, Color=[0,255,0], $
   Alignment=0.5, Location=[0.0, 1.05, 0.0], Font=helvetica14pt)
textModel->Add, plotTitle

    ; Create a trackball for surface rotations. Center it in
    ; the 400-by-400 window. Give it a 200 pixel diameter.

thisTrackball = OBJ_NEW('Trackball', [200, 200], 200)

   ; Create a palette for the surface.

thisPalette = Obj_New("IDLgrPalette")
thisPalette->LoadCT, colortable
thisPalette->GetProperty, Red=r, Green=g, Blue=b

    ; Create a surface object. Make it white.


  thisEllipse = OBJ_NEW('ellipsoid', rx=rx, ry=ry, rz=rz, transmatrix=transmatrix $
    , /tex_coords, color = [ 255, 255, 255 ])

  translucent_image, oImage
  thisEllipse -> SetProperty, texture_map = oImage







    ; Get the data ranges of the surface.

;thisSurface->GetProperty, XRange=xrange, YRange=yrange, ZRange=zrange
;thisSurface2->GetProperty, XRange=xrange2, YRange=yrange2, ZRange=zrange2
    ; Create axes objects for the surface. Color them green.
    ; Axes are created after the surface so the range can be
    ; set correctly. Note how I set the font to 10 pt helvetica.
rt=max([rx, ry, rz])
xrange=2*[-rt,rt]
xAxis = Obj_New("IDLgrAxis", 0, Color=[0,255,0], Ticklen=0.1, $
   Minor=4, Title=xtitle, Range=xrange, Exact=exact[0])
xAxis->GetProperty, Ticktext=xAxisText
xAxisText->SetProperty, Font=helvetica10pt
yrange=2*[-rt,rt]
yAxis = Obj_New("IDLgrAxis", 1, Color=[0,255,0], Ticklen=0.1, $
   Minor=4, Title=ytitle, Range=yrange, Exact=exact[1])
yAxis->GetProperty, Ticktext=yAxisText
yAxisText->SetProperty, Font=helvetica10pt
zrange=2*[-rt,rt]
zAxis = Obj_New("IDLgrAxis", 2, Color=[0,255,0], Ticklen=0.1, $
   Minor=4, Title=ztitle, Range=zrange, Exact=exact[2])
zAxis->GetProperty, Ticktext=zAxisText
zAxisText->SetProperty, Font=helvetica10pt

    ; The axes may not use exact axis scaling, so the ranges may
    ; have changed from what they were originally set to. Get
    ; and update the range variables.

xAxis->GetProperty, CRange=xrange
yAxis->GetProperty, CRange=yrange
zAxis->GetProperty, CRange=zrange
    ; Set scaling parameters for the surface and axes so that everything
    ; is scaled into the range -0.5 to 0.5. We do this so that when the
    ; surface is rotated we don't have to worry about translations. In
    ; other words, the rotations occur about the point (0,0,0).

xs = Normalize(xrange, Position=[pos[0], pos[1]])
ys = Normalize(yrange, Position=[pos[2], pos[3]])
zs = Normalize(zrange, Position=[pos[4], pos[5]])

    ; Scale the axes and place them in the coordinate space.
    ; Note that not all values in the Location keyword are
    ; used. (I've put really large values into the positions
    ; that are not being used to demonstate this.) For
    ; example, with the X axis only the Y and Z locations are used.

xAxis->SetProperty, Location=[9999.0, pos[2], pos[4]], XCoord_Conv=xs
yAxis->SetProperty, Location=[pos[0], 9999.0, pos[4]], YCoord_Conv=ys
zAxis->SetProperty, Location=[pos[0],  pos[3], 9999.0], ZCoord_Conv=zs

    ; Scale the surface.

;thisSurface->SetProperty, XCoord_Conv=xs, YCoord_Conv=ys, ZCoord_Conv=zs
;thisSurface2->SetProperty, XCoord_Conv=xs, YCoord_Conv=ys, ZCoord_Conv=zs
thisEllipse->SetProperty, XCoord_Conv=xs, YCoord_Conv=ys, ZCoord_Conv=zs


    ; Add the surface and axes objects to the model.

thisModel->Add, thisEllipse
thisModel->Add, xAxis
thisModel->Add, yAxis
thisModel->Add, zAxis

    ; Rotate the surface model to the standard surface view.

thisModel->Rotate,[1,0,0], -90  ; To get the Z-axis vertical.
thisModel->Rotate,[0,1,0],  30  ; Rotate it slightly to the right.
thisModel->Rotate,[1,0,0],  30  ; Rotate it down slightly.

; Create some lights to view the surface. Surfaces will look
; best if there is some ambient lighting to illuminate them
; uniformly, and some positional lights to give the surface
; definition. We will create three positional lights: one,
; non-rotating light will provide overhead definition. Two
; rotating lights will provide specific surface definition.
; Lights should be turned off or hidden if elevation shading
; is in effect.

    ; First create the ambient light. Don't turn it on too much,
    ; or the surface will appear washed out.

ambientLight = Obj_New('IDLgrLight', Type=0, Intensity=0.2, Hide=Keyword_Set(elevation))
thisModel->Add, ambientLight

    ; Shaded surfaces will not look shaded unless there is a
    ; positional light source to give the surface edges definition.
    ; This light will rotate with the surface.

rotatingLight = Obj_New('IDLgrLight', Type=1, Intensity=0.60, $
    Location=[xrange[1], yrange[1], 4*zrange[1]], $
    Direction=[xrange[0], yrange[0], zrange[0]], Hide=Keyword_Set(elevation))
thisModel->Add, rotatingLight

    ; Create a fill light source so you can see the underside
    ; of the surface. Otherwise, just the top surface will be visible.
    ; This light will also rotate with the surface.

fillLight = Obj_New('IDLgrLight', Type=1, Intensity=0.4, $
   Location=[(xrange[1]-xrange[0])/2.0, (yrange[1]-yrange[0])/2.0, -2*Abs(zrange[0])], $
   Direction=[(xrange[1]-xrange[0])/2.0, (yrange[1]-yrange[0])/2.0, zrange[1]], $
   Hide=Keyword_Set(elevation))
thisModel->Add, fillLight

    ; Create a non-rotating overhead side light.

nonrotatingLight = Obj_New('IDLgrLight', Type=1, Intensity=0.8, $
    Location=[-xrange[1], (yrange[1]-yrange[0])/2.0, 4*zrange[1]], $
    Direction=[xrange[1], (yrange[1]-yrange[0])/2.0, zrange[0]], $
    Hide=Keyword_Set(elevation))
nonrotatingModel = Obj_New('IDLgrModel')
nonrotatingModel->Add, nonrotatingLight

   ; Be sure to add the non-rotating model to the view, or it won't be visualized.

thisView->Add, nonrotatingModel

    ; Scale the light sources.

rotatingLight->SetProperty, XCoord_Conv=xs, YCoord_Conv=ys, ZCoord_Conv=zs
fillLight->SetProperty, XCoord_Conv=xs, YCoord_Conv=ys, ZCoord_Conv=zs
nonrotatingLight->SetProperty, XCoord_Conv=xs, YCoord_Conv=ys, ZCoord_Conv=zs

    ; Rotate the non-rotating model to the standard surface view.

nonrotatingModel->Rotate,[1,0,0], -90  ; To get the Z-axis vertical.
nonrotatingModel->Rotate,[0,1,0],  30  ; Rotate it slightly to the right.
nonrotatingModel->Rotate,[1,0,0],  30  ; Rotate it down slightly.

   ; Check for availability of GIF files.

thisVersion = Float(!Version.Release)
IF thisVersion LT 5.4 THEN haveGif = 1 ELSE haveGIF = 0

    ; Create the widgets to view the surface. Set expose events
    ; on the draw widget so that it refreshes itself whenever necessary.
    ; Button events are on to enable trackball movement.

tlb = Widget_Base(Title='Resizeable Window Surface Example', Column=1, $
   TLB_Size_Events=1, MBar=menubase)
drawID = Widget_Draw(tlb, XSize=400, YSize=400, Graphics_Level=2, Retain=0, $
   Expose_Events=1, Event_Pro='FSC_Surface_Draw_Events', Button_Events=1)

    ; Create FILE menu buttons for printing and exiting.

filer = Widget_Button(menubase, Value='File', /Menu)

   ; Create OUTPUT menu buttons for formatted output files. Use GIF
   ; files if available.

output = Widget_Button(filer, Value='Save As...', /Menu)
button = Widget_Button(output, Value='BMP File', $
   UValue='BMP', Event_Pro='FSC_Surface_Output')
button = Widget_Button(output, Value='EPS File', $
   UValue='EPS', Event_Pro='FSC_Surface_Output')
IF havegif THEN gif = Widget_Button(output, Value='GIF File', $
   UValue='GIF', Event_Pro='FSC_Surface_Output')
button = Widget_Button(output, Value='JPEG File', $
   UValue='JPEG', Event_Pro='FSC_Surface_Output')
button = Widget_Button(output, Value='PNG File', $
   UValue='PNG', Event_Pro='FSC_Surface_Output')
button = Widget_Button(output, Value='TIFF File', $
   UValue='TIFF', Event_Pro='FSC_Surface_Output')

printer = Widget_Button(filer, Value='Print', /Separator, $
   Event_Pro='FSC_Surface_Printing', /Menu)
dummy = Widget_Button(printer, Value='Vector Output (faster BW)', UValue='VECTOR')
dummy = Widget_Button(printer, Value='Bitmap Output (slower BW)', UValue='BITMAP')
dummy = Widget_Button(printer, Value='Full Color Printing (slower)', UValue='COLOR')

quitter = Widget_Button(filer, /Separator, Value='Exit', $
   Event_Pro='FSC_Surface_Exit')

   ; Create STYLE menu buttons for surface style.

style = Widget_Button(menubase, Value='Style', /Menu)
dummy = Widget_Button(style, Value='Dot Surface', $
   Event_Pro='FSC_Surface_Style', UValue='DOTS')
dummy = Widget_Button(style, Value='Wire Mesh', $
   Event_Pro='FSC_Surface_Style', UValue='MESH')
dummy = Widget_Button(style, Value='Solid', $
   Event_Pro='FSC_Surface_Style', UValue='SOLID')
dummy = Widget_Button(style, Value='Parallel X Lines', $
   Event_Pro='FSC_Surface_Style', UValue='XPARALLEL')
dummy = Widget_Button(style, Value='Parallel Y Lines', $
   Event_Pro='FSC_Surface_Style', UValue='YPARALLEL')
IF hidden_lines THEN hlValue = 'Hidden Lines OFF' ELSE hlValue='Hidden Lines ON'
dummy = Widget_Button(style, Value=hlvalue, $
   Event_Pro='FSC_Surface_Style', UValue='HIDDEN', /Separator)

IF elevation THEN BEGIN
   elevationID = Widget_Button(style, Value='Elevation Shading OFF', $
      /Separator, UValue='Elevation Shading ON', $
      Event_Pro='FSC_Surface_Elevation_Shading')
ENDIF ELSE BEGIN
   elevationID = Widget_Button(style, Value='Elevation Shading ON', $
      /Separator, UValue='Elevation Shading OFF', $
      Event_Pro='FSC_Surface_Elevation_Shading')
ENDELSE
colorsID = Widget_Button(style, Value='Elevation Colors...', $
   Event_Pro='FSC_Surface_Elevation_Colors')

   ; Create PROPERTIES menu buttons for surface properties.

properties = Widget_Button(menubase, Value='Properties', /Menu, $
   Event_Pro='FSC_Surface_Properties')

   ; Surface Color

scolorID = Widget_Button(properties, Value='Surface Color...', $
   UVALUE='SURFACE_COLOR')

IF elevation EQ 0 THEN BEGIN
   Widget_Control, colorsID, Sensitive = 0
   Widget_Control, scolorID, Sensitive = 1
ENDIF ELSE BEGIN
   Widget_Control, colorsID, Sensitive = 1
   Widget_Control, scolorID, Sensitive = 0
ENDELSE

   ; Background Color

bcolor = Widget_Button(properties, Value='Background Color', /Menu)
dummy = Widget_Button(bcolor, Value='Black', $
   Event_Pro='FSC_Surface_Properties', UValue='BBLACK')
dummy = Widget_Button(bcolor, Value='White', $
   Event_Pro='FSC_Surface_Properties', UValue='BWHITE')
dummy = Widget_Button(bcolor, Value='Charcoal', $
   Event_Pro='FSC_Surface_Properties', UValue='BCHARCOAL')
dummy = Widget_Button(bcolor, Value='Gray', $
   Event_Pro='FSC_Surface_Properties', UValue='BGRAY')

   ; Axes Color

acolor = Widget_Button(properties, Value='Axes Color', /Menu)
dummy = Widget_Button(acolor, Value='Black', $
   Event_Pro='FSC_Surface_Properties', UValue='ABLACK')
dummy = Widget_Button(acolor, Value='White', $
   Event_Pro='FSC_Surface_Properties', UValue='AWHITE')
dummy = Widget_Button(acolor, Value='Yellow', $
   Event_Pro='FSC_Surface_Properties', UValue='AYELLOW')
dummy = Widget_Button(acolor, Value='Green', $
   Event_Pro='FSC_Surface_Properties', UValue='AGREEN')
dummy = Widget_Button(acolor, Value='Navy Blue', $
   Event_Pro='FSC_Surface_Properties', UValue='ANAVY')

   ; Title Color

tcolor = Widget_Button(properties, Value='Title Color', /Menu)
dummy = Widget_Button(tcolor, Value='Black', $
   Event_Pro='FSC_Surface_Properties', UValue='TBLACK')
dummy = Widget_Button(tcolor, Value='White', $
   Event_Pro='FSC_Surface_Properties', UValue='TWHITE')
dummy = Widget_Button(tcolor, Value='Yellow', $
   Event_Pro='FSC_Surface_Properties', UValue='TYELLOW')
dummy = Widget_Button(tcolor, Value='Green', $
   Event_Pro='FSC_Surface_Properties', UValue='TGREEN')
dummy = Widget_Button(tcolor, Value='Navy Blue', $
   Event_Pro='FSC_Surface_Properties', UValue='TNAVY')

   ; Color Schemes.

dummy = Widget_Button(properties, Value='Black on White', /Separator, $
   Event_Pro='FSC_Surface_Properties', UValue='B/W')
dummy = Widget_Button(properties, Value='White on Black', $
   Event_Pro='FSC_Surface_Properties', UValue='W/B')
dummy = Widget_Button(properties, Value='Original Colors', $
   Event_Pro='FSC_Surface_Properties', UValue='ORIGINAL_COLORS')

   ; Original Axis rotation.

dummy = Widget_Button(properties, Value='Original Rotation', /Separator, $
   Event_Pro='FSC_Surface_Properties', UValue='ORIGINAL_T3D')

   ; Drag Quality.

dragID = Widget_Button(properties, Value='Drag Quality', /Separator, /Menu)
   dragLowID = Widget_Button(dragID, Value='Low', $
      Event_Pro='FSC_Surface_Properties', UValue='DRAG_LOW')
   dragMedID = Widget_Button(dragID, Value='Medium', $
      Event_Pro='FSC_Surface_Properties', UValue='DRAG_MEDIUM')
   dragHighID = Widget_Button(dragID, Value='High', $
      Event_Pro='FSC_Surface_Properties', UValue='DRAG_HIGH')
Widget_Control, dragHighID, Sensitive=0

lightID = Widget_Button(properties, Value='Light Controls...', $
   /Separator, Event_Pro='FSC_Surface_Light_Controls')

Widget_Control, tlb, /Realize

    ; Get the window destination object. The view will
    ; be drawn when the window is exposed.

Widget_Control, drawID, Get_Value=thisWindow

   ; Get a printer object for this graphic.

thisPrinter = Obj_New('IDLgrPrinter', Landscape=landscape)

   ; Create a container object to hold all the other
   ; objects. This will make it easy to free all the
   ; objects when we are finished with the program.

thisContainer = Obj_New('IDL_Container')

   ; Add created objects to the container.

thisContainer->Add, thisView
thisContainer->Add, thisPrinter
thisContainer->Add, thisTrackball
thisContainer->Add, xTitle
thisContainer->Add, yTitle
thisContainer->Add, zTitle
thisContainer->Add, xAxis
thisContainer->Add, yAxis
thisContainer->Add, zAxis
thisContainer->Add, thisEllipse
thisContainer->Add, oImage
thisContainer->Add, nonRotatingModel
thisContainer->Add, thisModel
thisContainer->Add, plotTitle
thisContainer->Add, helvetica10pt
thisContainer->Add, helvetica14pt
thisContainer->Add, thisPalette

   ; Get the current transformation matrix, so it can be restored.

thisModel->GetProperty, Transform=origTransform

   ; Create an INFO structure to hold needed program information.

info = { origTransform:origTransform, $       ; The transformation matrix.
         thisContainer:thisContainer, $       ; The object container.
         thisWindow:thisWindow, $             ; The window object.
         thisPrinter:thisPrinter, $           ; The printer object.
         thisEllipse:thisEllipse, $         ; The surface object.
         thisTrackball:thisTrackball, $       ; The trackball object.
         thisModel:thisModel, $               ; The model object.
         textModel:textModel, $               ; The model holding the instructions.
         xAxis:xAxis, $                       ; The X Axis object.
         yAxis:yAxis, $                       ; The Y Axis object.
         zAxis:zAxis, $                       ; The Z Axis object.
         nonRotatingLight:nonRotatingLight, $ ; The non-rotating light object.
         rotatingLight:rotatingLight, $       ; The rotating light object.
         fillLight:fillLight, $               ; The fill light object.
         ambientLight:ambientLight, $         ; The ambient light object.
         thisPalette:thisPalette, $           ; The surface color palette.
         colorsID:colorsID, $                 ; The color button for the texture map.
         drawID:drawID, $                     ; The widget identifier of the draw widget.
         colortable:colortable, $             ; The current color table.
         r:r, $                               ; The R values of the current color table.
         g:g, $                               ; The G values of the current color table.
         b:b, $                               ; The B values of the current color table.
         rx: rx, $
         ry: ry, $
         rz: rz, $
         transmatrix: transmatrix, $
         elevation:elevation, $               ; An elevation shading flag.
         elevationID:elevationID, $           ; The ID of the Elevation Shading button.
         scolorID:scolorID, $                 ; The surface color button ID.
         lightID:lightID, $                   ; The light control button ID.
         plotTitle:plotTitle, $               ; The plot title object.
         dragLowID:dragLowID, $               ; ID of Drag Quality Low button.
         dragMedID:dragMedID, $               ; ID of Drag Quality Medium button.
         dragHighID:dragHighID, $             ; ID of Drag Quality High button.
         dragQuality:2, $                     ; The current drag quality.
         surfIndex:!D.Table_Size-22, $        ; The surface color index.
         surfColor:[255,255,255], $           ; The surface color.
         landscape:landscape, $               ; Flag to indicate landscape printing.
         thisView:thisView }                  ; The view object.

   ; Store the info structure in the UValue of the TLB.

Widget_Control, tlb, Set_UValue=info, /No_Copy

   ; Call XManager. Set a cleanup routine so the objects
   ; can be freed upon exit from this program.

XManager, 'plot_ellipse', tlb, Cleanup='FSC_Surface_Cleanup', No_Block=(1 - Keyword_Set(block)), $
   Event_Handler='FSC_Surface_Resize', Group_Leader=groupLeader
END
;-------------------------------------------------------------------