; $Id: davegr2d__display.pro,v 1.31 2006/08/31 17:58:39 azuah Exp $
;###############################################################################
;
; CLASS NAME:
;   DaveGr2D
;
; PURPOSE:
;   The DaveGr2D class extends the DaveGr class to better handle
;   plotting of 2D data ie area and surface plots.
;
; CATEGORY:
;   Data Browser
;
; SUPERCLASSES:
;   DaveGr
;
; PUBLIC METHODS:
;   Display
;   Get
;   ReserMiscGrAttribs
;   Set
;
; AUTHOR:
;   Richard Tumanjong Azuah
;   NIST Center for Neutron Research
;   100 Bureau Drive, Gaithersburg, MD 20899
;   United States
;   azuah@nist.gov; (301) 9755604
;   Dec, 2002
;
; LICENSE:
;  The software in this file is written by an employee of 
;  National Institute of Standards and Technology 
;  as part of the DAVE software project.
;
;  The DAVE software package is not subject to copyright protection
;  and is in the public domain. It should be considered as an
;  experimental neutron scattering data reduction, visualization, and
;  analysis system. As such, the authors assume no responsibility
;  whatsoever for its use, and make no guarantees, expressed or
;  implied, about its quality, reliability, or any other
;  characteristic. The use of certain trade names or commercial
;  products does not imply any endorsement of a particular product,
;  nor does it imply that the named product is necessarily the best
;  product for the stated purpose. We would appreciate acknowledgment
;  if the DAVE software is used or if the code in this file is
;  included in another product.
;
;###############################################################################



;===============================================================================
; DaveGr2D::dispContour
;
; PURPOSE:
;   Create a contour plot of the object's dataset.
;
; PARAMETERS:
;   oScene [in] - the IDLgrScene object that is used as the root of
;                 the object graphics hierarchy that would be
;                 constructed.
;
; KEYWORDS:
;   xrange [in] - the x-axis limits (float) specified as a two
;                   element vector.
;
;   yrange [in] - the y-axis limits (float) specified as a two
;                   element vector.
;
;   zrange [in] - the z-axis limits (float) specified as a two
;                   element vector.
;
;   dispTitle [in] - if set, the main title will be displayed.
;
;   cbarLabel [in] - if set, a colorbar will be displayed.
;
;   winDim [in] - the dimensions of the IDLgr* destination object
;
;   normAspect [in] - if set, then make the data/screen size ratio
;                     the same for both x and y axis.
;
;   _EXTRA [in] - Allows keywords to be passed to various IDLgr* class
;                 methods.
;
; RETURN VALUE:
;   0 => failure
;   1 => success.
;
function DaveGr2D::dispContour, oScene,dispTitle=dispTitle,cbarLabel=cbarLabel,winDim=winDim $
  ,xrange=xrange,yrange=yrange,zrange=zrange,normAspect=normAspect,_EXTRA=etc
compile_opt idl2

retVal = 1

; if etc is empty, insert a dummy keyword into it
if (n_elements(etc) eq 0) then etc = {dummy:0}

if (self->count() lt 1) then return, 0 ; exit if no data is present

; Get data references and all graphic attributes
if (not self->get(dataRef=dataRef,position=0,nameTag=nameTag,bgColor=bgColor,fgColor=fgColor $
                  ,xColor=xColor,xFontVal=xFont,xFontSize=xFontSize,xLabel=xLabel,xLineSize=xLineSize $
                  ,yColor=yColor,yFontVal=yFont,yFontSize=yFontSize,yLabel=yLabel,yLineSize=yLineSize $
                  ,zColor=zColor,zFontVal=zFont,zFontSize=zFontSize,zLabel=zLabel $
                  ,fillflag=fillflag,shadeflag=shadeflag,nlevels=nlevels,palette=palette $
                  ,xMiscAttrib=xMiscAttribs,yMiscAttrib=yMiscAttribs,zMiscAttribs=zMiscAttribs $
                  ,xmin=xmin0,xmax=xmax0,ymin=ymin0,ymax=ymax0,zmin=zmin0,zmax=zmax0 $
                  ,cLabelFlag=cLabelFlag,cLabelInt=cLabelInt,cLabelDist=cLabelDist,downhillFlag=downhillFlag $
                  ,cThick=cThick,cLinestyle=cLinestyle $
                  ,title=title,label=labelArray,nlabel=nlabel)) then return, 0

; get the x,y and  z-axis data
if (not dataRef->get(xval=xval,yval=yval,zval=zval,xtype=xtype,ytype=ytype)) then return, 0

; If it is not in point-mode, convert x-axis to point mode
if (strmid(strupcase(xtype),0,4) eq 'HIST') then begin
    len = n_elements(xval)
    index = lindgen(len-1)
    xvalMid = 0.5*(xval[index]+xval[index+1]) ; ==> mid values of the bin boundaries
    xval = xvalMid
endif

; If it is not in point-mode, convert y-axis to point mode
if (strmid(strupcase(ytype),0,4) eq 'HIST') then begin
    len = n_elements(yval)
    index = lindgen(len-1)
    yvalMid = 0.5*(yval[index]+yval[index+1]) ; ==> mid values of the bin boundaries
    yval = yvalMid
endif

; set/calculate miscellaneous axes attributes
; x-axis
xscale = xMiscAttribs.scalefactor
xscale = 1
cropXdata = 1
case n_elements(xrange) of
    0: begin
        case xMiscAttribs.useCustomFlag of
            0: begin            ; using default data range
                ;dx = 0.05*(xmax-xmin)
                ;xmin = (xmin - dx)*xscale
                ;xmax = (xmax + dx)*xscale
                xmin = xmin0 * xscale
                xmax = xmax0 * xscale
                xMajorTicks = 5
                cropXdata = 0
                ;xetc = {extend:1}
            end
            1: begin            ; using custom range specified in axis property dialog
                xmin = xMiscAttribs.minimum * xscale
                xmax = xMiscAttribs.maximum * xscale
                xMajorInterval = xMiscAttribs.tickInterval * xscale
                ;xetc = {exact:1}
            end
        endcase
    end
    else: begin                    ; probably using a rubberband box setting
        xmin = xrange[0]
        xmax = xrange[1]
        ;xMajorInterval = 0.25*(xmax-xmin) ; ==>majorTicks = 5
        xMajorTicks = 5
        ;xetc = {exact:1}
    end
endcase

; Make sure axis range contains more than one data point
if (cropXdata) then begin
    index = where((xval ge xmin) and (xval le xmax))
    if (n_elements(index) gt 1) then begin ; don't allow ranges that include only one data point
        xval = xval[index]
        zval = zval[index,*]
        xetc = {exact:1}
    endif else xmin=min(xval,max=xmax)
endif

; Don't allow axis limits which fall outside the data range.
xmin = xmin > xmin0
xmax = xmax < xmax0

; y -axis
yscale = yMiscAttribs.scalefactor
yscale = 1
cropYdata = 1
case n_elements(yrange) of
    0: begin
        case yMiscAttribs.useCustomFlag of
            0: begin            ; using default data range
                ymin = ymin0 * yscale
                ymax = ymax0 * yscale
                yMajorTicks = 5
                cropYdata = 0
                ;yetc = {extend:1}
            end
            1: begin            ; using custom range specified in axis property dialog
                ymin = yMiscAttribs.minimum * yscale
                ymax = yMiscAttribs.maximum * yscale
                yMajorInterval = yMiscAttribs.tickInterval * yscale
                ;yetc = {exact:1}
            end
        endcase
    end
    else: begin                    ; probably using a rubberband box setting
        ymin = yrange[0]
        ymax = yrange[1]
        yMajorTicks = 5
        ;yetc = {exact:1}
    end
endcase

; Make sure axis range contains more than one data point
if (cropYdata) then begin
    index = where((yval ge ymin) and (yval le ymax))
    if (n_elements(index) gt 1) then begin ; don't allow ranges that include only one data point
        yval = yval[index]
        zval = zval[*,index]
        xetc = {exact:1}
    endif else ymin=min(yval,max=ymax)
endif

; Don't allow axis limits which fall outside the data range.
ymin = ymin > ymin0
ymax = ymax < ymax0

xr = xmax-xmin                  ; x data range
yr = ymax-ymin                  ; y data range

xpadFrac = 0.25                 ; fraction of xr allowed as x padding
ypadFrac = 0.25                 ; ditto for yr
xpadMul = (cbarLabel eq 1)? 2.35 : 1.5 ; xpad multiplier
ypadMul = 2.0                   ;ypad multiplier

if (normAspect eq 1) then begin
    ;; require the ratio of data to the part of the viewplane use for
    ;; display of the data to be the same for both x and y axis
    xres = xr/winDim[0]
    yres = yr/winDim[1]
    if (cbarLabel eq 0) then xpadMul = 2.0
    ypadMul = xpadMul
    padFrac = xpadFrac
    if (xres lt yres) then xr = yres*winDim[0]
    if (yres lt xres) then yr = xres*winDim[1]
endif

xpad = xpadFrac*xr
vplaneX0 = xmin - xpad
vplaneW = xr + xpadMul*xpad ;(cbarLabel eq 1)? xr + 2.30*xpad : xr + 1.5*xpad
xMinorTicks = xMiscAttribs.minorTicks
xLog = (xmin gt 0.0)? xMiscAttribs.logFlag : 0 ; range must be +ve for log
xFormat = getFormatCode(xmin,xmax,xMiscAttribs.decimalPlaces)
xTickDir = xMiscAttribs.tickDir
xetc = {exact:1}


ypad = ypadFrac*yr
vplaneY0 = ymin - ypad
vplaneH = yr + ypadMul*ypad ;yr + 2.0*ypad
yMinorTicks = yMiscAttribs.minorTicks
yLog = (ymin gt 0.0)? yMiscAttribs.logFlag : 0 ; range must be +ve for log
yFormat = getFormatCode(ymin,ymax,yMiscAttribs.decimalPlaces)
yTickDir = yMiscAttribs.tickDir
yetc = {exact:1}

xTickLen = 0.01 * xMiscAttribs.tickLen * yr
yTickLen = 0.01 * yMiscAttribs.tickLen * xr

; z -axis
zscale = zMiscAttribs.scalefactor
zscale = 1
zrangeFlag = 0
case n_elements(zrange) of
    0: begin
        case zMiscAttribs.useCustomFlag of
            0: begin            ; using default data range
                zmin = zmin0 * zscale
                zmax = zmax0 * zscale
                ;zMajorTicks = 5
                ;zetc = {extend:1}
            end
            1: begin            ; using custom range specified in axis property dialog
                zmin = zMiscAttribs.minimum * zscale
                zmax = zMiscAttribs.maximum * zscale
                ;zMajorInterval = zMiscAttribs.tickInterval * zscale
                ;zetc = {exact:1}
            end
        endcase
    end
    else: begin                    ; data range set using widget
        zmin = zrange[0]
        zmax = zrange[1]
        zrangeFlag = 1
        ;zMajorTicks = 5
        ;zetc = {exact:1}
    end
endcase

; If xval is decreasing then reverse it. This is necessary because the
; axes and viewport are created with this assmption.
if (xval[n_elements(xval)-1] lt xval[0]) then begin
    xval = reverse(xval)
    zval = reverse(zval,1)
endif

; If yval is decreasing then reverse it.This is necessary because the
; axes and viewport are created with this assmption.
if (yval[n_elements(yval)-1] lt yval[0]) then begin
    yval = reverse(yval)
    zval = reverse(zval,2)
endif

; Create a container to hold misc object references that are not part
; of the IDL graphics object hierarchy
oMiscCont = obj_new('IDL_Container')
oScene->add, oMiscCont

; Create a new view and add it to scene
oView = obj_new('IDLgrView',units=0,viewplane_rect=[vplaneX0,vplaneY0,vplaneW,vplaneH], $
                color=bgColor,name='MAINVIEW')
oScene->add, oView

; IDLgrModel to hold ALL graphic atoms.
oModel = obj_new('IDLgrModel',name='DG2DMODEL')

; Create (lower and upper) x-axis + an IDLgrModel to hold them
;--------------------------------
oXaModel = obj_new('IDLgrModel',name='AMODEL',/select_target) ; make it selectable
oModel->add, oXaModel
oXaxis1 = obj_new('IDLgrAxis',0,location=[xmin,ymin],range=[xmin,xmax],color=xColor, $
                  ticklen=xTicklen,name='AXIS',major=xMajorTicks,minor=xMinorTicks,tickdir=xTickDir, $
                  uvalue={objRef:self,datInd:0},thick=xLineSize,tickformat=xFormat, $
                  tickinterval=xMajorInterval,log=xLog,/exact)
oXaxis2 = obj_new('IDLgrAxis',0,location=[xmin,ymax],range=[xmin,xmax],color=xColor, $
                  ticklen=xTicklen,name='AXIS',major=xMajorTicks,minor=xMinorTicks,tickdir=1-xTickDir, $
                  /notext,uvalue={objRef:self,datInd:0},thick=xLineSize,tickformat=xFormat, $
                  tickinterval=xMajorInterval,log=xLog,/exact)
oXaModel->add, oXaxis1
oXaModel->add, oXaxis2


; Create (left and right) y-axis + an IDLgrModel to hold them
;----------------------------------
oYaModel = obj_new('IDLgrModel',name='AMODEL',/select_target) ; make it selectable
oModel->add, oYaModel
oYaxis1 = obj_new('IDLgrAxis',1,location=[xmin,ymin],range=[ymin,ymax],color=yColor, $
                  ticklen=yTicklen,name='AXIS',major=yMajorTicks,minor=yMinorTicks,tickdir=yTickDir, $
                  uvalue={objRef:self,datInd:1},thick=yLineSize,tickformat=yFormat, $
                  tickinterval=yMajorInterval,log=yLog,/exact)
oYaxis2 = obj_new('IDLgrAxis',1,location=[xmax,ymin],range=[ymin,ymax],color=yColor, $
                  ticklen=yTicklen,name='AXIS',major=yMajorTicks,minor=yMinorTicks,tickdir=1-yTickDir, $
                  /notext,uvalue={objRef:self,datInd:1},thick=yLineSize,tickformat=yFormat, $
                  tickinterval=yMajorInterval,log=yLog,/exact)
oYaModel->add, oYaxis1
oYaModel->add, oYaxis2

; x-axes labels
oXfont = obj_new('IDLgrFont',name=xFont,size=xFontSize)
oMiscCont->add,oXfont
if (n_elements(xlabel) eq 0) then xlabel='X axis title'
oXlabel = obj_new('IDLgrText',xlabel,name='AXISLABEL',font=oXfont,/enable_formatting)
oXaxis1->setProperty, title=oXlabel
oMiscCont->add, oXlabel
oXaxis1->getProperty, ticktext=ttRef ; get the axis tick text object
ttRef->setProperty, font=oXfont ; and assign it the same font as for the label

; y-axes labels
oYfont = obj_new('IDLgrFont',name=yFont,size=yFontSize)
oMiscCont->add,oYfont
if (n_elements(ylabel) eq 0) then ylabel='Y axis title'
oYlabel = obj_new('IDLgrText',ylabel,name='AXISLABEL',font=oYfont,/enable_formatting)
oYaxis1->setProperty, title=oYlabel
oMiscCont->add, oYlabel
oYaxis1->getProperty, ticktext=ttRef ; get the axis tick text object
ttRef->setProperty, font=oYfont ; and assign it the same font as for the label


; the contour object
; 
oCModel = obj_new('IDLgrModel',name='CONTMODEL',/select_target) ; make it selectable
oModel->add, oCModel

; Make all data which fall below/above the minimum/maximum to be 
; same as the minimum/maximum
if (zrangeFlag) then begin
    small = 0.00001*(zmax-zmin)
    index = where(zval le zmin, count)
    if (count gt 0) then zval[index] = zmin+small
    index = where(zval ge zmax, count)
    if (count gt 0) then zval[index] = zmax-small
endif
cbarZmin = zmin
cbarZmax = zmax
if (zMiscAttribs.logFlag) then begin ; using log scale
    ;; take logs of all quantities
    zval = alog10(zval)
    zmin = alog10(zmin)
    zmax = alog10(zmax)
endif

etc = create_struct(etc, 'fill',fillflag)
etc = create_struct(etc, 'palette',palette)
etc = create_struct(etc, 'shading',shadeflag)
palette->getProperty,red=red,green=green,blue=blue,n_colors=ncolors
stepc = float((ncolors-1)/(nlevels-1))
ccolors = bindgen(nlevels)
for i=0,nlevels-1 do ccolors[i] = fix(i*stepc)
shaderange = [ccolors[0],ccolors[nlevels-1]]
etc = create_struct(etc, 'c_color',ccolors)
etc = create_struct(etc, 'shade_range',shaderange)
stepv = (zmax - zmin)/(nlevels - 1)
cvalues = findgen(nlevels)*stepv + zmin
etc = create_struct(etc, 'c_value',cvalues)
etc = create_struct(etc, 'depth_offset',1)

if (cLabelInt eq 1) then begin
   cLabelShow = intarr(nLevels) + 1
endif else begin
   modFlags = (indgen(nLevels)+1) mod cLabelInt
   index = where(modFlags eq 1,cnt)
   cLabelShow = intarr(nLevels)
   if (cnt gt 0) then cLabelShow[index] = 1
endelse
etc = create_struct(etc, 'c_label_show',cLabelShow*cLabelFlag)

etc = create_struct(etc, 'downhill',downhillFlag)
etc = create_struct(etc, 'c_label_interval',cLabelDist)
etc = create_struct(etc, 'c_linestyle',cLinestyle)
etc = create_struct(etc, 'c_thick',cThick)

oContour = obj_new('IDLgrContour',zval,geomx=xval,geomy=yval,color=fgColor,geomz=-0.1,/planar, $
                   uvalue={objRef:self,datInd:0},name='CONTOUR', $
                   min_value=zmin,max_value=zmax,_EXTRA=etc)

if (fillFlag) then begin
   ;; No need to scale to normalised units as the view (oView) is created
   ;; in data space.
   oContour->getProperty,xrange=cxr,yrange=cyr
   xs = scale_coord(cxr,[xmin,xmax])
   ys = scale_coord(cyr,[ymin,ymax])
   oContour->setProperty,xcoord_conv=xs,ycoord_conv=ys
endif

oCModel->add, oContour

; Colorbar
if (cbarLabel eq 1) then begin
    cbarMajor = 5
    cbarDecimalPlaces = 2
    tickValues = findgen(cbarMajor)*(cbarZmax-cbarZmin)/(cbarMajor-1) + cbarZmin
    formatCode = getFormatCode(tickValues[0],tickValues[cbarMajor-1],cbarDecimalPlaces)
    tickValues = strtrim(string(tickValues,format=formatCode),2)
    cbarText = obj_new('IDLgrText',tickValues)
    oMiscCont->add,cbarText

    etc1 = {show_axis:2,major:cbarMajor}
    etc2 = {tickText:cbarText}
endif
oColorbar = obj_new('IDLgrColorbar',name='COLORBAR',palette=palette,_EXTRA=etc1)
oColorbar->getProperty,xrange=cxr,yrange=cyr
xs = scale_coord(cxr,[xmax+0.05*xpad,xmax+0.35*xpad])
ys = scale_coord(cyr,[ymin,ymax])

oColorbar->setProperty,xcoord_conv=xs,ycoord_conv=ys,tickText=cbarText,_EXTRA=etc2

oModel->add, oColorbar


; Main plot title
xs = scale_coord([0,1],[vplaneX0,vplaneX0+vplaneW])
ys = scale_coord([0,1],[vplaneY0,vplaneY0+vplaneH])

if ((dispTitle eq 1) and (strtrim(title.text,2) ne '')) then begin
    oLabelModel = obj_new('IDLgrModel',name='LABMODEL',/select_target) ; make it selectable
    oModel->add, oLabelModel

    oTitleFont = obj_new('IDLgrFont',name=title.font,size=title.fontsize)
    oMiscCont->add,oTitleFont   ; so it can be properly cleaned up
    oTitle = obj_new('IDLgrText',title.text,name='LABEL',font=oTitleFont,/enable_formatting $
                     ,location=title.position,alignment=title.alignment,color=title.color $
                     ,xcoord_conv=xs,ycoord_conv=ys,uvalue={objRef:self,datInd:-1})
    oLabelModel->add,oTitle
endif

; Miscellaneous text labels
while (nlabel gt 0) do begin
    nlabel = nlabel - 1
    newLabel = labelArray[nlabel]
    oLabelModel = obj_new('IDLgrModel',name='LABMODEL',/select_target) ; make it selectable
    oModel->add, oLabelModel
    oLabelFont = obj_new('IDLgrFont',name=newLabel.font,size=newLabel.fontsize)
    oMiscCont->add,oLabelFont   ; so it can be properly cleaned up
    oLabel = obj_new('IDLgrText',newLabel.text,name='LABEL',font=oLabelFont,/enable_formatting $
                     ,location=newLabel.position,alignment=newLabel.alignment,color=newLabel.color $
                     ,xcoord_conv=xs,ycoord_conv=ys,uvalue={objRef:self,datInd:nlabel})
    oLabelModel->add,oLabel
endwhile

;----------------------------------
; Finally add the model to the view
oView->add, oModel
return, 1

; Destroy  oModel if an error condition was encountered
DestroyModel:
if (obj_valid(oModel)) then obj_destroy, oModel
return, 0

return, retVal

end
;-------------------------------------------------------------------------------



;===============================================================================
; DaveGr2D::dispImage
;
; PURPOSE:
;   Create an image plot of the object's dataset.
;
; PARAMETERS:
;   oScene [in] - the IDLgrScene object that is used as the root of
;                 the object graphics hierarchy that would be
;                 constructed.
;
; KEYWORDS:
;   xrange [in] - the x-axis limits (float) specified as a two
;                   element vector.
;
;   yrange [in] - the y-axis limits (float) specified as a two
;                   element vector.
;
;   zrange [in] - the z-axis limits (float) specified as a two
;                   element vector.
;
;   dispTitle [in] - if set, the main title will be displayed.
;
;   cbarLabel [in] - if set, a colorbar will be displayed.
;
;   winDim [in] - the dimensions of the IDLgr* destination object
;
;   normAspect [in] - if set, then make the data/screen size ratio
;                     the same for both x and y axis.
;
;   _EXTRA [in] - Allows keywords to be passed to various IDLgr* class
;                 methods.
;
; RETURN VALUE:
;   0 => failure
;   1 => success.
;
function DaveGr2D::dispImage, oScene,dispTitle=dispTitle,cbarLabel=cbarLabel,winDim=winDim $
  ,xrange=xrange,yrange=yrange,zrange=zrange,normAspect=normAspect,_EXTRA=etc
compile_opt idl2

retVal = 1

; if etc is empty, insert a dummy keyword into it
if (n_elements(etc) eq 0) then etc = {dummy:0}

if (self->count() lt 1) then return, 0 ; exit if no data is present

; Get data references and all graphic attributes
if (not self->get(dataRef=dataRef,position=0,nameTag=nameTag,bgColor=bgColor,fgColor=fgColor $
                  ,xColor=xColor,xFontVal=xFont,xFontSize=xFontSize,xLabel=xLabel,xLineSize=xLineSize $
                  ,yColor=yColor,yFontVal=yFont,yFontSize=yFontSize,yLabel=yLabel,yLineSize=yLineSize $
                  ,zColor=zColor,zFontVal=zFont,zFontSize=zFontSize,zLabel=zLabel $
                  ,fillflag=fillflag,shadeflag=shadeflag,nlevels=nlevels $
                  ,xMiscAttrib=xMiscAttribs,yMiscAttrib=yMiscAttribs,zMiscAttribs=zMiscAttribs $
                  ,xmin=xmin0,xmax=xmax0,ymin=ymin0,ymax=ymax0,zmin=zmin0,zmax=zmax0 $
                  ,title=title,label=labelArray,nlabel=nlabel,interpFlag=interpFlag,palette=palette $
                 )) then return, 0


; get the x,y and  z-axis data
if (not dataRef->get(xval=xval,yval=yval,zval=zval,xtype=xtype,ytype=ytype)) then return, 0

; If it is not in point-mode, convert x-axis to point mode
if (strmid(strupcase(xtype),0,4) eq 'HIST') then begin
    len = n_elements(xval)
    index = lindgen(len-1)
    xvalMid = 0.5*(xval[index]+xval[index+1]) ; ==> mid values of the bin boundaries
    xval = xvalMid
endif

; If it is not in point-mode, convert y-axis to point mode
if (strmid(strupcase(ytype),0,4) eq 'HIST') then begin
    len = n_elements(yval)
    index = lindgen(len-1)
    yvalMid = 0.5*(yval[index]+yval[index+1]) ; ==> mid values of the bin boundaries
    yval = yvalMid
endif


; set/calculate miscellaneous axes attributes
; x-axis
xscale = xMiscAttribs.scalefactor
xscale = 1
cropXdata = 1
case n_elements(xrange) of
    0: begin
        case xMiscAttribs.useCustomFlag of
            0: begin            ; using default data range
                ;dx = 0.05*(xmax-xmin)
                ;xmin = (xmin - dx)*xscale
                ;xmax = (xmax + dx)*xscale
                xmin = xmin0 * xscale
                xmax = xmax0 * xscale
                xMajorTicks = 5
                cropXdata = 0
                ;xetc = {extend:1}
            end
            1: begin            ; using custom range specified in axis property dialog
                xmin = xMiscAttribs.minimum * xscale
                xmax = xMiscAttribs.maximum * xscale
                xMajorInterval = xMiscAttribs.tickInterval * xscale
                ;xetc = {exact:1}
            end
        endcase
    end
    else: begin                    ; probably using a rubberband box setting
        xmin = xrange[0]
        xmax = xrange[1]
        ;xMajorInterval = 0.25*(xmax-xmin) ; ==>majorTicks = 5
        xMajorTicks = 5
        ;xetc = {exact:1}
    end
endcase

; Make sure axis range contains more than one data point
if (cropXdata) then begin
    index = where((xval ge xmin) and (xval le xmax))
    if (n_elements(index) gt 1) then begin ; don't allow ranges that include only one data point
        xval = xval[index]
        zval = zval[index,*]
        xetc = {exact:1}
    endif else xmin=min(xval,max=xmax)
endif

; Don't allow axis limits which fall outside the data range.
xmin = xmin > xmin0
xmax = xmax < xmax0

; y -axis
yscale = yMiscAttribs.scalefactor
yscale = 1
cropYdata = 1
case n_elements(yrange) of
    0: begin
        case yMiscAttribs.useCustomFlag of
            0: begin            ; using default data range
                ymin = ymin0 * yscale
                ymax = ymax0 * yscale
                yMajorTicks = 5
                cropYdata = 0
                ;yetc = {extend:1}
            end
            1: begin            ; using custom range specified in axis property dialog
                ymin = yMiscAttribs.minimum * yscale
                ymax = yMiscAttribs.maximum * yscale
                yMajorInterval = yMiscAttribs.tickInterval * yscale
                ;yetc = {exact:1}
            end
        endcase
    end
    else: begin                    ; probably using a rubberband box setting
        ymin = yrange[0]
        ymax = yrange[1]
        yMajorTicks = 5
        ;yetc = {exact:1}
    end
endcase

; Make sure axis range contains more than one data point
if (cropYdata) then begin
    index = where((yval ge ymin) and (yval le ymax))
    if (n_elements(index) gt 1) then begin ; don't allow ranges that include only one data point
        yval = yval[index]
        zval = zval[*,index]
        xetc = {exact:1}
    endif else ymin=min(yval,max=ymax)
endif

; Don't allow axis limits which fall outside the data range.
ymin = ymin > ymin0
ymax = ymax < ymax0


xr = xmax-xmin                  ; x data range
yr = ymax-ymin                  ; y data range

xpadFrac = 0.25                 ; fraction of xr allowed as x padding
ypadFrac = 0.25                 ; ditto for yr
xpadMul = (cbarLabel eq 1)? 2.35 : 1.5 ; xpad multiplier
ypadMul = 2.0                   ;ypad multiplier

if (normAspect eq 1) then begin
    ;; require the ratio of data to the part of the viewplane use for
    ;; display of the data to be the same for both x and y axis
    xres = xr/winDim[0]
    yres = yr/winDim[1]
    if (cbarLabel eq 0) then xpadMul = 2.0
    ypadMul = xpadMul
    padFrac = xpadFrac
    if (xres lt yres) then xr = yres*winDim[0]
    if (yres lt xres) then yr = xres*winDim[1]
endif

xpad = xpadFrac*xr
vplaneX0 = xmin - xpad
vplaneW = xr + xpadMul*xpad ;(cbarLabel eq 1)? xr + 2.30*xpad : xr + 1.5*xpad
xMinorTicks = xMiscAttribs.minorTicks
xLog = (xmin gt 0.0)? xMiscAttribs.logFlag : 0 ; range must be +ve for log
xFormat = getFormatCode(xmin,xmax,xMiscAttribs.decimalPlaces)
xTickDir = xMiscAttribs.tickDir
xetc = {exact:1}


ypad = ypadFrac*yr
vplaneY0 = ymin - ypad
vplaneH = yr + ypadMul*ypad ;yr + 2.0*ypad
yMinorTicks = yMiscAttribs.minorTicks
yLog = (ymin gt 0.0)? yMiscAttribs.logFlag : 0 ; range must be +ve for log
yFormat = getFormatCode(ymin,ymax,yMiscAttribs.decimalPlaces)
yTickDir = yMiscAttribs.tickDir
yetc = {exact:1}

xTickLen = 0.01 * xMiscAttribs.tickLen * yr
yTickLen = 0.01 * yMiscAttribs.tickLen * xr

; z -axis
zscale = zMiscAttribs.scalefactor
zscale = 1
zrangeFlag = 0
case n_elements(zrange) of
    0: begin
        case zMiscAttribs.useCustomFlag of
            0: begin            ; using default data range
                zmin = zmin0 * zscale
                zmax = zmax0 * zscale
                ;zMajorTicks = 5
                ;zetc = {extend:1}
            end
            1: begin            ; using custom range specified in axis property dialog
                zmin = zMiscAttribs.minimum * zscale
                zmax = zMiscAttribs.maximum * zscale
                ;zMajorInterval = zMiscAttribs.tickInterval * zscale
                ;zetc = {exact:1}
            end
        endcase
    end
    else: begin                    ; probably using a rubberband box setting
        zmin = zrange[0]
        zmax = zrange[1]
        zrangeFlag = 1
        ;zMajorTicks = 5
        ;zetc = {exact:1}
    end
endcase

; If xval is decreasing then reverse it. This is necessary because the
; axes and viewport are created with this assmption.
if (xval[n_elements(xval)-1] lt xval[0]) then begin
    xval = reverse(xval)
    zval = reverse(zval,1)
endif

; If yval is decreasing then reverse it.This is necessary because the
; axes and viewport are created with this assmption.
if (yval[n_elements(yval)-1] lt yval[0]) then begin
    yval = reverse(yval)
    zval = reverse(zval,2)
endif


; Create a container to hold misc object references that are not part
; of the IDL graphics object hierarchy
oMiscCont = obj_new('IDL_Container')
oScene->add, oMiscCont

; Create a new view and add it to scene
oView = obj_new('IDLgrView',units=0,viewplane_rect=[vplaneX0,vplaneY0,vplaneW,vplaneH],color=bgColor)
oScene->add, oView

; IDLgrModel to hold ALL graphic atoms.
oModel = obj_new('IDLgrModel',name='DG2DMODEL')


; Create (lower and upper) x-axis + an IDLgrModel to hold them
;--------------------------------
oXaModel = obj_new('IDLgrModel',name='AMODEL',/select_target) ; make it selectable
;oModel->add, oXaModel
oXaxis1 = obj_new('IDLgrAxis',0,location=[xmin,ymin],range=[xmin,xmax],color=xColor, $
                  ticklen=xTicklen,name='AXIS',major=xMajorTicks,minor=xMinorTicks,tickdir=xTickDir, $
                  uvalue={objRef:self,datInd:0},thick=xLineSize,tickformat=xFormat, $
                  tickinterval=xMajorInterval,log=xLog,/exact)
oXaxis2 = obj_new('IDLgrAxis',0,location=[xmin,ymax],range=[xmin,xmax],color=xColor, $
                  ticklen=xTicklen,name='AXIS',major=xMajorTicks,minor=xMinorTicks,tickdir=1-xTickDir, $
                  /notext,uvalue={objRef:self,datInd:0},thick=xLineSize,tickformat=xFormat, $
                  tickinterval=xMajorInterval,log=xLog,/exact)
oXaModel->add, oXaxis1
oXaModel->add, oXaxis2


; Create (left and right) y-axis + an IDLgrModel to hold them
;----------------------------------
oYaModel = obj_new('IDLgrModel',name='AMODEL',/select_target) ; make it selectable
;oModel->add, oYaModel
oYaxis1 = obj_new('IDLgrAxis',1,location=[xmin,ymin],range=[ymin,ymax],color=yColor, $
                  ticklen=yTicklen,name='AXIS',major=yMajorTicks,minor=yMinorTicks,tickdir=yTickDir, $
                  uvalue={objRef:self,datInd:1},thick=yLineSize,tickformat=yFormat, $
                  tickinterval=yMajorInterval,log=yLog,/exact)
oYaxis2 = obj_new('IDLgrAxis',1,location=[xmax,ymin],range=[ymin,ymax],color=yColor, $
                  ticklen=yTicklen,name='AXIS',major=yMajorTicks,minor=yMinorTicks,tickdir=1-yTickDir, $
                  /notext,uvalue={objRef:self,datInd:1},thick=yLineSize,tickformat=yFormat, $
                  tickinterval=yMajorInterval,log=yLog,/exact)
oYaModel->add, oYaxis1
oYaModel->add, oYaxis2

; x-axes labels
oXfont = obj_new('IDLgrFont',name=xFont,size=xFontSize)
oMiscCont->add,oXfont
if (n_elements(xlabel) eq 0) then xlabel='X axis title'
oXlabel = obj_new('IDLgrText',xlabel,name='AXISLABEL',font=oXfont,/enable_formatting)
oXaxis1->setProperty, title=oXlabel
oMiscCont->add, oXlabel
oXaxis1->getProperty, ticktext=ttRef ; get the axis tick text object
ttRef->setProperty, font=oXfont ; and assign it the same font as for the label

; y-axes labels
oYfont = obj_new('IDLgrFont',name=yFont,size=yFontSize)
oMiscCont->add,oYfont
if (n_elements(ylabel) eq 0) then ylabel='Y axis title'
oYlabel = obj_new('IDLgrText',ylabel,name='AXISLABEL',font=oYfont,/enable_formatting)
oYaxis1->setProperty, title=oYlabel
oMiscCont->add, oYlabel
oYaxis1->getProperty, ticktext=ttRef ; get the axis tick text object
ttRef->setProperty, font=oYfont ; and assign it the same font as for the label

; Make all data which fall below/above the minimum/maximum to be 
; same as the minimum/maximum
if (zrangeFlag) then begin
    small = 0.00001*(zmax-zmin)
    index = where(zval le zmin, count)
    if (count gt 0) then zval[index] = zmin+small
    index = where(zval ge zmax, count)
    if (count gt 0) then zval[index] = zmax-small
endif
cbarZmin = zmin
cbarZmax = zmax
if (zMiscAttribs.logFlag) then begin ; using log scale
    ;; take logs of all quantities
    zval = alog10(zval)
    zmin = alog10(zmin)
    zmax = alog10(zmax)
endif


; the image object
;etc = create_struct(etc, 'interpolate',interpFlag)
oIModel = obj_new('IDLgrModel',name='IMAGEMODEL',/select_target) ; make it selectable

oImage = obj_new('IDLgrImage',bytscl(zval,min=zmin,max=zmax),uvalue={objRef:self,datInd:0},name='IMAGE' $
                 ,dimensions=[xr,yr],palette=palette,interpolate=interpFlag)
oImage->getProperty,xrange=cxr,yrange=cyr
xs = scale_coord(cxr,[xmin,xmax])
ys = scale_coord(cyr,[ymin,ymax])
oImage->setProperty,xcoord_conv=xs,ycoord_conv=ys

oIModel->add, oImage


oModel->add, oIModel
oModel->add, oYaModel
oModel->add, oXaModel


; Colorbar

; oColorbar = obj_new('IDLgrColorbar',name='COLORBAR',palette=palette)
; oColorbar->getProperty,xrange=cxr,yrange=cyr
; xs = scale_coord(cxr,[xmax+0.05*xpad,xmax+0.35*xpad])
; ys = scale_coord(cyr,[ymin,ymax])
; oColorbar->setProperty,xcoord_conv=xs,ycoord_conv=ys

if (cbarLabel eq 1) then begin
    cbarMajor = 5
    cbarDecimalPlaces = 2
    tickValues = findgen(cbarMajor)*(cbarZmax-cbarZmin)/(cbarMajor-1) + cbarZmin
    formatCode = getFormatCode(tickValues[0],tickValues[cbarMajor-1],cbarDecimalPlaces)
    tickValues = strtrim(string(tickValues,format=formatCode),2)
    cbarText = obj_new('IDLgrText',tickValues)
    oMiscCont->add,cbarText

    etc1 = {show_axis:2,major:cbarMajor}
    etc2 = {tickText:cbarText}
endif
oColorbar = obj_new('IDLgrColorbar',name='COLORBAR',palette=palette,_EXTRA=etc1)
oColorbar->getProperty,xrange=cxr,yrange=cyr
xs = scale_coord(cxr,[xmax+0.05*xpad,xmax+0.35*xpad])
ys = scale_coord(cyr,[ymin,ymax])

oColorbar->setProperty,xcoord_conv=xs,ycoord_conv=ys,tickText=cbarText,_EXTRA=etc2

oModel->add, oColorbar


; Main plot title
xs = scale_coord([0,1],[vplaneX0,vplaneX0+vplaneW])
ys = scale_coord([0,1],[vplaneY0,vplaneY0+vplaneH])

if ((dispTitle eq 1) and (strtrim(title.text,2) ne '')) then begin
    oLabelModel = obj_new('IDLgrModel',name='LABMODEL',/select_target) ; make it selectable
    oModel->add, oLabelModel

    oTitleFont = obj_new('IDLgrFont',name=title.font,size=title.fontsize)
    oMiscCont->add,oTitleFont   ; so it can be properly cleaned up
    oTitle = obj_new('IDLgrText',title.text,name='LABEL',font=oTitleFont,/enable_formatting $
                     ,location=title.position,alignment=title.alignment,color=title.color $
                     ,xcoord_conv=xs,ycoord_conv=ys,uvalue={objRef:self,datInd:-1})
    oLabelModel->add,oTitle
endif

; Miscellaneous text labels
while (nlabel gt 0) do begin
    nlabel = nlabel - 1
    newLabel = labelArray[nlabel]
    oLabelModel = obj_new('IDLgrModel',name='LABMODEL',/select_target) ; make it selectable
    oModel->add, oLabelModel
    oLabelFont = obj_new('IDLgrFont',name=newLabel.font,size=newLabel.fontsize)
    oMiscCont->add,oLabelFont   ; so it can be properly cleaned up
    oLabel = obj_new('IDLgrText',newLabel.text,name='LABEL',font=oLabelFont,/enable_formatting $
                     ,location=newLabel.position,alignment=newLabel.alignment,color=newLabel.color $
                     ,xcoord_conv=xs,ycoord_conv=ys,uvalue={objRef:self,datInd:nlabel})
    oLabelModel->add,oLabel
endwhile


; Finally add the model to the view
oView->add, oModel
return, 1

; Destroy  oModel if an error condition was encountered
DestroyModel:
if (obj_valid(oModel)) then obj_destroy, oModel
return, 0


return, retVal

end
;-------------------------------------------------------------------------------



;===============================================================================
; DaveGr2D::dispSurface
;
; PURPOSE:
;   Create a surface plot of the object's dataset.
;
; PARAMETERS:
;   oScene [in] - the IDLgrScene object that is used as the root of
;                 the object graphics hierarchy that would be
;                 constructed.
;
; KEYWORDS:
;   xrange [in] - the x-axis limits (float) specified as a two
;                   element vector.
;
;   yrange [in] - the y-axis limits (float) specified as a two
;                   element vector.
;
;   zrange [in] - the z-axis limits (float) specified as a two
;                   element vector.
;
;   dispTitle [in] - if set, the main title will be displayed.
;
;   transform [in] - specifies a transformation matrix to be applied
;                    to the model containing the surface plot.
;
;   _EXTRA [in] - Allows keywords to be passed to various IDLgr* class
;                 methods.
;
; RETURN VALUE:
;   0 => failure
;   1 => success.
;
function DaveGr2D::dispSurface, oScene,xrange=xrange,yrange=yrange,zrange=zrange $
  ,dispTitle=dispTitle,transform=transform, _EXTRA=etc

retVal = 1

; if etc is empty, insert a dummy keyword into it
if (n_elements(etc) eq 0) then etc = {dummy:0}

if (self->count() lt 1) then return, 0 ; exit if no data is present

; Get data references and all graphic attributes
if (not self->get(dataRef=dataRef,position=0,nameTag=nameTag,bgColor=bgColor,fgColor=fgColor $
                  ,xColor=xColor,xFontVal=xFont,xFontSize=xFontSize,xLabel=xLabel,xLineSize=xLineSize $
                  ,yColor=yColor,yFontVal=yFont,yFontSize=yFontSize,yLabel=yLabel,yLineSize=yLineSize $
                  ,zColor=zColor,zFontVal=zFont,zFontSize=zFontSize,zLabel=zLabel,zLineSize=zLineSize $
                  ,shadeFlag=shadeFlag,skirtFlag=skirtFlag,skirtValue=skirtValue,style=style $
                  ,bottomFlag=bottomFlag,bottomColor=bottomColor,hiddenLineFlag=hlFlag $
                  ,ambLightInt=alInt,posLightInt=plInt,posLightLoc=plLoc,posLightDir=plDir $
                  ,ambLightFlag=alFlag,posLightFlag=plFlag,vertShadeFlag=vertShadeFlag,palette=palette $
                  ,xMiscAttrib=xMiscAttribs,yMiscAttrib=yMiscAttribs,zMiscAttribs=zMiscAttribs $
                  ,xmin=xmin0,xmax=xmax0,ymin=ymin0,ymax=ymax0,zmin=zmin0,zmax=zmax0 $
                  ,title=title,label=labelArray,nlabel=nlabel $
                 )) then return, 0


; get the x,y and  z-axis data
if (not dataRef->get(xval=xval,yval=yval,zval=zval,xtype=xtype,ytype=ytype)) then return, 0

; If it is not in point-mode, convert x-axis to point mode
if (strmid(strupcase(xtype),0,4) eq 'HIST') then begin
    len = n_elements(xval)
    index = lindgen(len-1)
    xvalMid = 0.5*(xval[index]+xval[index+1]) ; ==> mid values of the bin boundaries
    xval = xvalMid
endif

; If it is not in point-mode, convert y-axis to point mode
if (strmid(strupcase(ytype),0,4) eq 'HIST') then begin
    len = n_elements(yval)
    index = lindgen(len-1)
    yvalMid = 0.5*(yval[index]+yval[index+1]) ; ==> mid values of the bin boundaries
    yval = yvalMid
endif

; set/calculate miscellaneous axes attributes
; x-axis
xscale = xMiscAttribs.scalefactor
xscale = 1
cropXdata = 1
case n_elements(xrange) of
    0: begin
        case xMiscAttribs.useCustomFlag of
            0: begin            ; using default data range
                xmin = xmin0 * xscale
                xmax = xmax0 * xscale
                xMajorTicks = 5
                cropXdata = 0
                xetc = {extend:1}
            end
            1: begin            ; using custom range specified in axis property dialog
                xmin = xMiscAttribs.minimum * xscale
                xmax = xMiscAttribs.maximum * xscale
                xMajorInterval = xMiscAttribs.tickInterval * xscale
                xetc = {exact:1}
            end
        endcase
    end
    else: begin                    ; probably using a rubberband box setting
        xmin = xrange[0]
        xmax = xrange[1]
        xMajorTicks = 5
        xetc = {exact:1}
    end
endcase

; Make sure axis range contains more than one data point
if (cropXdata) then begin
    index = where((xval ge xmin) and (xval le xmax))
    if (n_elements(index) gt 1) then begin ; don't allow ranges that include only one data point
        xval = xval[index]
        zval = zval[index,*]
        xetc = {exact:1}
    endif else xmin=min(xval,max=xmax)
endif

; Don't allow axis limits which fall outside the data range.
xmin = xmin > xmin0
xmax = xmax < xmax0

; If using log scale for x-axis, 
; - make sure range it is +ve
; - convert to log scale
logPossible = 1
if (xMiscAttribs.logFlag) then begin ; using log scale
    ;; zrange must be +ve
    if (xmin le 0.0) then begin
        xmin = 0.00001*xmax
        index = where(xval ge xmin, count)
        if (count gt 0) then begin
            xval = xval[index]
            zval = zval[index,*]
            xmin = min(xval,max=xmax)
            zmin = min(zval,max=zmax)
        endif else logPossible=0
    endif
    if (logPossible) then begin
        ;; take logs of all quantities    
        xval = alog10(xval)
        ;xmin = alog10(xmin)
        ;xmax = alog10(xmax)
    endif
endif

xr = xmax-xmin
xMinorTicks = xMiscAttribs.minorTicks
xLog = (logPossible)? xMiscAttribs.logFlag : 0 ; range must be +ve for log
xFormat = getFormatCode(xmin,xmax,xMiscAttribs.decimalPlaces)
xTickDir = xMiscAttribs.tickDir


; y -axis
yscale = yMiscAttribs.scalefactor
yscale = 1
cropYdata = 1
case n_elements(yrange) of
    0: begin
        case yMiscAttribs.useCustomFlag of
            0: begin            ; using default data range
                ymin = ymin0 * yscale
                ymax = ymax0 * yscale
                yMajorTicks = 5
                cropYdata = 0
                yetc = {extend:1}
            end
            1: begin            ; using custom range specified in axis property dialog
                ymin = yMiscAttribs.minimum * yscale
                ymax = yMiscAttribs.maximum * yscale
                yMajorInterval = yMiscAttribs.tickInterval * yscale
                yetc = {exact:1}
            end
        endcase
    end
    else: begin                    ; probably using a rubberband box setting
        ymin = yrange[0]
        ymax = yrange[1]
        yMajorTicks = 5
        yetc = {exact:1}
    end
endcase

; Make sure axis range contains more than one data point
if (cropYdata) then begin
    index = where((yval ge ymin) and (yval le ymax))
    if (n_elements(index) gt 1) then begin ; don't allow ranges that include only one data point
        yval = yval[index]
        zval = zval[*,index]
        xetc = {exact:1}
    endif else ymin=min(yval,max=ymax)
endif

; Don't allow axis limits which fall outside the data range.
ymin = ymin > ymin0
ymax = ymax < ymax0

; If using log scale for y-axis, 
; - make sure range it is +ve
; - convert to log scale
logPossible = 1
if (yMiscAttribs.logFlag) then begin ; using log scale
    ;; yrange must be +ve
    if (ymin le 0.0) then begin
        ymin = 0.00001*ymax
        index = where(yval ge ymin, count)
        if (count gt 0) then begin
            yval = yval[index]
            zval = zval[*,index]
            ymin = min(yval,max=ymax)
            zmin = min(zval,max=zmax)
        endif else logPossible = 0
    endif
    if (logPossible) then begin
        ;; take logs of all quantities    
        yval = alog10(yval)
        ;ymin = alog10(ymin)
        ;ymax = alog10(ymax)
    endif
endif

yr = ymax-ymin
yMinorTicks = yMiscAttribs.minorTicks
yLog = (logPossible)? yMiscAttribs.logFlag : 0 ; range must be +ve for log
yFormat = getFormatCode(ymin,ymax,yMiscAttribs.decimalPlaces)
yTickDir = yMiscAttribs.tickDir


; z -axis
zscale = zMiscAttribs.scalefactor
zscale = 1
case n_elements(zrange) of
    0: begin
        case zMiscAttribs.useCustomFlag of
            0: begin            ; using default data range
                zmin = zmin0 * zscale
                zmax = zmax0 * zscale
                zMajorTicks = 5
                zetc = {extend:1}
            end
            1: begin            ; using custom range specified in axis property dialog
                zmin = zMiscAttribs.minimum * zscale
                zmax = zMiscAttribs.maximum * zscale
                zMajorInterval = zMiscAttribs.tickInterval * zscale
                zetc = {exact:1}
            end
        endcase
    end
    else: begin                    ; probably using a rubberband box setting
        zmin = zrange[0]
        zmax = zrange[1]
        zMajorTicks = 5
        zetc = {exact:1}
    end
endcase

; If using log scale for z-axis, make sure range is +ve
logPossible = 1
if (zMiscAttribs.logFlag) then begin ; using log scale
    ;; zrange must be +ve
    if (zmin le 0.0) then begin
        zmin = 0.00001*(zmax-zmin)
        index = where(zval le zmin, count)
        if (count gt 0) then begin
            zval[index] = zmin
        endif else logPossible = 0
    endif
    if (logPossible) then begin
        ;; take logs of all quantities    
        zval = alog10(zval)
        zminlog = alog10(zmin)
        zmaxlog = alog10(zmax)
    endif
endif


zr = zmax-zmin
zMinorTicks = zMiscAttribs.minorTicks
zLog = (logPossible)? zMiscAttribs.logFlag : 0 ; range must be +ve for log
zFormat = getFormatCode(zmin,zmax,zMiscAttribs.decimalPlaces)
zTickDir = zMiscAttribs.tickDir




xvp = [-0.50,0.65]
yvp = [-0.50,0.50]
zvp = [-0.45,0.80]

xrvp = xvp[1] - xvp[0]
yrvp = yvp[1] - yvp[0]
zrvp = zvp[1] - zvp[0]

xTickLen = 0.01 * xMiscAttribs.tickLen * yrvp
yTickLen = 0.01 * yMiscAttribs.tickLen * xrvp
zTickLen = 0.01 * zMiscAttribs.tickLen * xrvp


; Create a container to hold misc object references that are not part
; of the IDL graphics object hierarchy
oMiscCont = obj_new('IDL_Container')
oScene->add, oMiscCont

; Create a new view and add it to scene
;oView = obj_new('IDLgrView',units=0,viewplane_rect=[vplaneX0,vplaneY0,vplaneW,vplaneH] $
;                ,color=bgColor,zclip=[zmax+zpad,zmin-zpad])
oView = obj_new('IDLgrView',viewplane_rect=[-1.0,-1.0,2.0,2.0],zclip=[1.0,-1.0] $
                ,eye=10.0,color=bgColor)
oScene->add, oView

; IDLgrModel to hold ALL graphic atoms.
oModel = obj_new('IDLgrModel',name='DG2DMODEL')

; Create x-axis + an IDLgrModel to hold it
;--------------------------------
oXaModel = obj_new('IDLgrModel',name='AMODEL',/select_target) ; make it selectable
;oModel->add, oXaModel
oXaxis = obj_new('IDLgrAxis',0,range=[xmin,xmax],color=xColor $
                 ,ticklen=xTicklen,name='AXIS',major=xMajorTicks,minor=xMinorTicks,tickdir=xTickDir $
                 ,uvalue={objRef:self,datInd:0},thick=xLineSize,tickformat=xFormat $
                 ,tickinterval=xMajorInterval,log=xLog,/exact)

;oXaxis = obj_new('IDLgrAxis',0,range=[xmin,xmax],color=xColor $
;                  ,ticklen=xTicklen,name='AXIS',major=xMajor,minor=xMinor,tickdir=0 $
;                  ,uvalue={objRef:self,datInd:0},thick=xLineSize,_EXTRA=xetc)

oXaxis->getProperty,crange=cxr
xs = scale_coord(cxr,xvp)
oXaxis->setProperty,location=[xvp[0],yvp[0],zvp[0]],xcoord_conv=xs

oXaModel->add, oXaxis


; Create y-axis + an IDLgrModel to hold it
;----------------------------------
oYaModel = obj_new('IDLgrModel',name='AMODEL',/select_target) ; make it selectable
;oModel->add, oYaModel
oYaxis = obj_new('IDLgrAxis',1,range=[ymin,ymax],color=yColor $
                 ,ticklen=yTicklen,name='AXIS',major=yMajorTicks,minor=yMinorTicks,tickdir=yTickDir $
                 ,uvalue={objRef:self,datInd:1},thick=yLineSize,tickformat=yFormat $
                 ,tickinterval=yMajorInterval,log=yLog,/exact)

;oYaxis = obj_new('IDLgrAxis',1,range=[ymin,ymax],color=yColor $
;                  ,ticklen=yTicklen,name='AXIS',major=yMajor,minor=yMinor,tickdir=0 $
;                  ,uvalue={objRef:self,datInd:1},thick=yLineSize,_EXTRA=yetc)

oYaxis->getProperty,crange=cyr
ys = scale_coord(cyr,yvp)
oYaxis->setProperty,location=[xvp[0],yvp[0],zvp[0]],ycoord_conv=ys

oYaModel->add, oYaxis

; Create z-axis + an IDLgrModel to hold them
;----------------------------------
oZaModel = obj_new('IDLgrModel',name='AMODEL',/select_target) ; make it selectable
;oModel->add, oZaModel
oZaxis = obj_new('IDLgrAxis',2,range=[zmin,zmax],color=zColor $
                 ,ticklen=zTicklen,name='AXIS',major=zMajor,minor=zMinor,tickdir=zTickDir $
                 ,uvalue={objRef:self,datInd:2},thick=zLineSize,tickformat=zFormat $
                 ,tickinterval=zMajorInterval,log=zLog,/exact)

oZaxis->getProperty,crange=czr
zs = scale_coord(czr,zvp)
oZaxis->setProperty,location=[xvp[0],yvp[1],zvp[0]],zcoord_conv=zs

oZaModel->add, oZaxis

; x-axes labels
oXfont = obj_new('IDLgrFont',name=xFont,size=xFontSize)
oMiscCont->add,oXfont
if (n_elements(xlabel) eq 0) then xlabel='X axis title'
oXlabel = obj_new('IDLgrText',xlabel,name='XAXISLABEL',font=oXfont,/enable_formatting)
oXaxis->setProperty, title=oXlabel
oMiscCont->add, oXlabel
oXaxis->getProperty, ticktext=ttRef ; get the axis tick text object
ttRef->setProperty, font=oXfont ; and assign it the same font as for the label

; y-axes labels
oYfont = obj_new('IDLgrFont',name=yFont,size=yFontSize)
oMiscCont->add,oYfont
if (n_elements(ylabel) eq 0) then ylabel='Y axis title'
oYlabel = obj_new('IDLgrText',ylabel,name='YAXISLABEL',font=oYfont,/enable_formatting)
oYaxis->setProperty, title=oYlabel
oMiscCont->add, oYlabel
oYaxis->getProperty, ticktext=ttRef ; get the axis tick text object
ttRef->setProperty, font=oYfont ; and assign it the same font as for the label

; z-axes labels
oZfont = obj_new('IDLgrFont',name=zFont,size=zFontSize)
oMiscCont->add,oZfont           ; so it can be properly cleaned up
if (n_elements(zlabel) eq 0) then zlabel='Z axis title'
oZlabel = obj_new('IDLgrText',zlabel,name='ZAXISLABEL',font=oZfont,/enable_formatting)
oZaxis->setProperty, title=oZlabel
oMiscCont->add, oZlabel
oZaxis->getProperty, ticktext=ttRef ; get the axis tick text object
ttRef->setProperty, font=oZfont ; and assign it the same font as for the label

; Create a surface model to hold the surface object
oSurfModel = obj_new('IDLgrModel',name='SURFMODEL',/select_target) ; make it selectable
;oModel->add,oSurfModel

; Determine various attributes for surface object
etc = create_struct(etc,'style',style) ; style
etc = create_struct(etc,'shading',shadeFlag) ; shading - flat or gouraud
if (vertShadeFlag eq 1) then begin ; use elevation color shading?
    zsize = Size(zval, /Dimensions)
    vcolors = reform(bytscl(zval,/NAN,min=zminlog,max=zmaxlog),zsize[0]*zsize[1])
    etc = create_struct(etc,'vert_colors',vcolors)
endif else etc = create_struct(etc,'vert_colors',0)
etc = create_struct(etc,'hidden_lines',hlFlag) ; hidden line removal
etc = create_struct(etc,'show_skirt',skirtFlag)
etc = create_struct(etc,'skirt',skirtValue)
if (bottomFlag eq 1) then $
  etc = create_struct(etc,'bottom',bottomColor)
;etc = create_struct(etc,'depth_offset',0)

; Create the surface object
oSurface = obj_new('IDLgrSurface',zval,xval,yval,uvalue={objRef:self,datInd:0},name='SURFACE' $
                   ,palette=palette,color=fgColor,min_value=zminlog,max_value=zmaxlog,_EXTRA=etc)
oSurface->getProperty,xrange=cxr,yrange=cyr,zrange=czr
xs = scale_coord(cxr,xvp)
ys = scale_coord(cyr,yvp)
zs = scale_coord(czr,zvp)
oSurface->setProperty,xcoord_conv=xs,ycoord_conv=ys,zcoord_conv=zs

oSurfModel->add, oSurface

; Make a rotational model
; All model/atoms to be rotated are placed here
oRotModel = obj_new('IDLgrModel',name='ROTMODEL') ; it is NOT selectable
oModel->add, oRotModel

oRotModel->add, oSurfModel
oRotModel->add, oXaModel
oRotModel->add, oYaModel
oRotModel->add, oZaModel

;xs = scale_coord([0,1],[vplaneX0,vplaneX0+vplaneW])
;ys = scale_coord([0,1],[vplaneY0,vplaneY0+vplaneH])

; Main plot title
if ((dispTitle eq 1) and (strtrim(title.text,2) ne '')) then begin
    oLabelModel = obj_new('IDLgrModel',name='LABMODEL',/select_target) ; make it selectable
    oModel->add, oLabelModel

    oTitleFont = obj_new('IDLgrFont',name=title.font,size=title.fontsize)
    oMiscCont->add,oTitleFont   ; so it can be properly cleaned up
    oTitle = obj_new('IDLgrText',title.text,name='LABEL',font=oTitleFont,/enable_formatting $
                     ,location=title.position,alignment=title.alignment,color=title.color $
                     ,uvalue={objRef:self,datInd:-1})
;                     ,xcoord_conv=xs,ycoord_conv=ys,uvalue={objRef:self,datInd:-1})
    oLabelModel->add,oTitle
endif

; Miscellaneous text labels
while (nlabel gt 0) do begin
    nlabel = nlabel - 1
    newLabel = labelArray[nlabel]
    oLabelModel = obj_new('IDLgrModel',name='LABMODEL',/select_target) ; make it selectable
    oModel->add, oLabelModel
    oLabelFont = obj_new('IDLgrFont',name=newLabel.font,size=newLabel.fontsize)
    oMiscCont->add,oLabelFont   ; so it can be properly cleaned up
    oLabel = obj_new('IDLgrText',newLabel.text,name='LABEL',font=oLabelFont,/enable_formatting $
                     ,location=newLabel.position,alignment=newLabel.alignment,color=newLabel.color $
                     ;,xcoord_conv=xs,ycoord_conv=ys $
                     ,uvalue={objRef:self,datInd:nlabel})
    oLabelModel->add,oLabel
    ;oRotModel->add, oLabelModel ; Make model rotatable
endwhile





; Perform required rotations
if (n_elements(transform) gt 0) then begin
    oRotModel->setProperty, transform=transform
endif else begin
    oRotModel->Rotate,[1,0,0], -90 ; To get the Z-axis vertical.
    oRotModel->Rotate,[0,1,0],  30 ; Rotate it slightly to the right.
    oRotModel->Rotate,[1,0,0],  30 ; Rotate it down slightly.
endelse

; Ambient light source
if (alFlag) then begin
    oALight = Obj_New('IDLgrLight',type=0,intensity=alInt)
    oModel->add, oALight
endif

; Positional light source
;if (plFlag) then begin
oPLight = obj_new('IDLgrLight',type=1,name='POSLIGHT',hide=1-plFlag,intensity=plInt, $
                  location=plLoc,direction=plDir)
oModel->add, oPLight
;endif

; Finally add the model to the view
oView->add, oModel
return, 1

; Destroy  oModel if an error condition was encountered
DestroyModel:
if (obj_valid(oModel)) then obj_destroy, oModel
return, 0

return, retVal

end
;-------------------------------------------------------------------------------



;===============================================================================
; DaveGr2D::Display
;
; PURPOSE:
;   Create an area or surface plot of the object's dataset.
;
; PARAMETERS:
;   oDest [in] - a valid IDL graphics destination object
;
;   oScene [in] - the IDLgrScene object that is used as the root of
;                 the object graphics hierarchy that would be
;                 constructed.
;
; KEYWORDS:
;   _EXTRA [in] - Allows keywords to be passed on to the other private
;                 methods in this file.
;
pro DaveGr2D::display, oDest, oScene, _EXTRA=etc

; reset !error_state structure, ie clear all errors
message,/reset

; ensure (required) number of parameters are present
if (n_params() ne 2) then begin
    !error_state.code = -1
    !error_state.msg = "DaveGr2D::display: Two parameters required for this procedure call"
    return
endif

; Don't proceed any further if there is no data
if (self->count() lt 1) then begin
    !error_state.code = -1
    !error_state.msg = "DaveGr2D::display: No data to display!"
    return
endif

; oDest should be a valid destination object
valid = ['IDLGRWINDOW','IDLGRBUFFER','IDLGRCLIPBOARD','IDLGRPRINTER','IDLGRVRML']
res = where(vartype(oDest) eq valid, okay)
if (not okay) then begin
    !error_state.code = -1
    !error_state.msg = "DaveGr2D::display: parameter 1 (oDest) must be a valid IDL Object Graphics destination object"
    return
endif

; oScene should be a valid IDL Graphics container object
valid = ['IDLGRMODEL','IDLGRSCENE','IDLGRVIEW','IDLGRVIEWGROUP']
res = where(vartype(oScene) eq valid, okay)
if (not okay) then begin
    !error_state.code = -1
    !error_state.msg = "DaveGr2D::display: parameter 2 (oScene) must be a valid IDL Object Graphics container object"
    return
endif

; Get contents of the current scene and destroy them
objects = oScene->get(/all, count=nObjects)
if (nObjects gt 0) then begin
    print,'**** Destroying Scene...'
    obj_destroy, objects
endif

if (not self->get(plotType=plotType)) then return

; if etc is empty, insert a dummy keyword into it
if (n_elements(etc) eq 0) then etc = {dummy:0}

oDest->getProperty, dimensions=dim
;;aspect = dim[0]/dim[1]
case strupcase(plotType) of
    'CONTOUR': retVal = self->dispContour(oScene,winDim=dim,_EXTRA=etc)
    'IMAGE': retVal = self->dispImage(oScene,winDim=dim,_EXTRA=etc)
    'SURFACE': retVal = self->dispSurface(oScene,_EXTRA=etc)
endcase

if (retVal ne 1) then begin
    print,'DaveGr2D::display: ERROR - DATA DISPLAY UNSUCCESSFUL'
endif else $
  oDest->draw, oScene

return

end
