; docformat = 'rst'
;
; NAME:
;   cgAspect
;
; PURPOSE:
;   The purpose of this function is to calculate a position in a graphics window with
;   a specified aspect ratio (ysize/xsize).
;
;******************************************************************************************;
;                                                                                          ;
;  Copyright (c) 2013, by Fanning Software Consulting, Inc. All rights reserved.           ;
;                                                                                          ;
;  Redistribution and use in source and binary forms, with or without                      ;
;  modification, are permitted provided that the following conditions are met:             ;
;                                                                                          ;
;      * Redistributions of source code must retain the above copyright                    ;
;        notice, this list of conditions and the following disclaimer.                     ;
;      * Redistributions in binary form must reproduce the above copyright                 ;
;        notice, this list of conditions and the following disclaimer in the               ;
;        documentation and/or other materials provided with the distribution.              ;
;      * Neither the name of Fanning Software Consulting, Inc. nor the names of its        ;
;        contributors may be used to endorse or promote products derived from this         ;
;        software without specific prior written permission.                               ;
;                                                                                          ;
;  THIS SOFTWARE IS PROVIDED BY FANNING SOFTWARE CONSULTING, INC. ''AS IS'' AND ANY        ;
;  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES    ;
;  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT     ;
;  SHALL FANNING SOFTWARE CONSULTING, INC. BE LIABLE FOR ANY DIRECT, INDIRECT,             ;
;  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED    ;
;  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;         ;
;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND             ;
;  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT              ;
;  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS           ;
;  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                            ;
;******************************************************************************************;
;
;+
; The purpose of this function is to calculate a position in a graphics window with
; a specified aspect ratio (ysize/xsize).
;
; :Categories:
;    Utility
;    
; :Params:
;    aspect_ratio: in, required, type=float
;         The requested aspect ratio. Be careful to specify this as a float.
;       
; :Keywords:
;    align: in, optional, type=string, default='center'
;         Normally, the output position is centered in the intial input position. This
;         keyword can alter the placement of the final position with respect to the initial
;         position. Allowed values are "top", "right", "bottom", "left", and "center".
;    aspect: in, optional, type=array
;         This keyword provides an alternative way of specifying the `aspect_rato` value. Pass
;         this keyword any 2D array, and the aspect ratio will be calculated to match the 2D array.
;    position: in, optional, type=array
;         Use this keyword to specify a four-element normalized starting position for calculating
;         the final position in the graphics window. By default, the staring position is given 
;         by [0.0, 0.0, 1.0, 1.0] (the entire window). If the `Single_Plot` keyword is set, the
;         default position is given by [0.125, 0.125, 0.9, 0.9].
;    single_plot: in, optional, type=boolean, default=0
;         Set this keyword to change the default starting `Position` to [0.125, 0.125, 0.9, 0.9].
;    waspect: in, optional, type=float
;         Set this keyword to the window aspect ratio. If not used, the window aspect is calculated from
;         the current graphics window as Float(!D.Y_VSize) / !D.X_VSize.
;         
; :Examples:
;    Here is how to use this program to display two differently sized images aligned at their tops::
;        img1 = cgDemoData(7)
;        img2 = congrid(img1, 360, 180)
;        cgDisplay, 900, 450
;        
;        pos = cgLayout([2,1], OYMargin=[4, 11], OXMargin=[5, 8], XGap=6)
;        pos1 = pos[*,0]
;        img1pos = cgAspect(Position=pos1, Aspect=img1, Align='top')
;        cgImage, img1, CTIndex=3, /AXES, POSITION=img1pos, OPOSITION=op
;        cgColorbar, CTIndex=3, /Fit    
;            
;        pos2 = pos[*,1]
;        img2pos = cgAspect(Position=pos2, Aspect=img2, Align='top')
;        cgImage, img2, /AXES, POSITION=img2pos, CTIndex=2, /NoErase
;        cgColorbar, CTIndex=2, /Fit  
;       
; :Author:
;    FANNING SOFTWARE CONSULTING::
;       David W. Fanning 
;       1645 Sheely Drive
;       Fort Collins, CO 80526 USA
;       Phone: 970-221-0438
;       E-mail: david@idlcoyote.com
;       Coyote's Guide to IDL Programming: http://www.idlcoyote.com
;
; :History:
;     Change History::
;        Written, 12 February 2013 by David W. Fanning.
;
; :Copyright:
;     Copyright (c) 2013, Fanning Software Consulting, Inc.
;-
FUNCTION cgAspect, aspect_ratio, $
   ALIGN=align, $
   ASPECT=aspect, $
   POSITION=position, $
   SINGLE_PLOT=single_plot, $
   WASPECT=waspect
   
   ; Return to caller in the event of an error.
   On_Error, 2
   
   ; Is there a 2D array to use to set the requested aspect_ratio?
   IF N_Elements(aspect) NE 0 THEN BEGIN
        dims = Size(aspect, /Dimensions)
        aspect_ratio = Double(dims[1]) / dims[0]
   ENDIF
   
   ; Do you have an aspect ratio?
   IF N_Elements(aspect_ratio) EQ 0 THEN BEGIN
      Message, 'Calling Syntax:  position = cgAspect(aspect_ratio)', /Informational
      RETURN, Replicate(0.0,4)
   ENDIF ELSE aspect_ratio = Double(aspect_ratio)
   
   ; Do we need a position in the window?
   IF N_Elements(position) EQ 0 THEN BEGIN
      IF Keyword_Set(single_plot) THEN BEGIN
          position = [0.125d, 0.125d, 0.9d, 0.9d]
      ENDIF ELSE BEGIN
          position = [0.0d, 0.0d, 1.0d, 1.0d]
      ENDELSE
   ENDIF
   
   ; Do we need a window aspect ratio?
   IF N_Elements(waspect) EQ 0 THEN BEGIN
      waspect = Double(!D.Y_VSize) / !D.X_VSize
   ENDIF
   
   ; Do you have an alignment?
   IF N_Elements(align) EQ 0 THEN align = 'center'
   align = StrUpCase(align)
   
   ; Find the proposed size in pixels without aspect considerations.
   xpixSize = (position[2] - position[0]) * !D.X_VSize
   ypixSize = (position[3] - position[1]) * !D.Y_VSize
   
   ; Try to fit the width. If you can't maintain the aspect ratio, fit the height.
   trialX = xpixSize
   trialY = trialX * aspect_ratio
   IF trialY GT ypixSize THEN BEGIN
       trialY = ypixSize
       trialX = trialY / aspect_ratio
   ENDIF
   
   ; Recalculate the position in the window.
   pos = FltArr(4)
   pos[0] = (((xpixSize - trialX) / 2.0) / !D.X_VSize) + position[0]
   pos[2] = pos[0] + (trialX/FLOAT(!D.X_VSize))
   pos[1] = (((ypixSize - trialY) / 2.0) / !D.Y_VSize)  + position[1]
   pos[3] = pos[1] + (trialY/FLOAT(!D.Y_VSize))

   CASE align OF
    
       'RIGHT': BEGIN
           xdiff = position[2] - pos[2]
           pos[0] = pos[0] + xdiff
           pos[2] = position[2]
           END
    
       'TOP': BEGIN
           ydiff = position[3] - pos[3]
           pos[3] = position[3]
           pos[1] = pos[1] + ydiff
           END
           
       'LEFT': BEGIN
           xdiff = pos[0] - position[0]
           pos[0] = position[0]
           pos[2] = position[2] - xdiff
           END
           
       'BOTTOM': BEGIN
           ydiff = pos[1] - position[1]
           pos[1] = position[1]
           pos[3] = pos[3] - ydiff
           END
          
       'CENTER': 
           
        ELSE: Message, 'Unknown alignment parameter: ' + align
       
   ENDCASE
   
   ; Return the position
   RETURN, pos
   
END