;+
; NAME:
;       violin_plot.pro
;
; PURPOSE:
;       This procedure plots violin plots.
;
; CATEGORY:
;       Graphics
;
; CALLING SEQUENCE:
;       violin_plot, x_pos=x_pos, y_pos=y_pos, pdf_loc=pdf_loc, pdf_amp=pdf_amp
;
; INPUTS:
;    COLOR, FILL, ORIENT, PDF_AMP, PDF_LOC, PSYM, THICK, X_POS, Y_POS
;
; KEYWORD PARAMETERS:
;    COLOR:  An optional scalar integer specifying the color index value of the 
;        color to use.  The default !p.color.
;    FILL:  If set then the violin shapes are filled.  The default is 0, in 
;        which only the outline is plotted.
;    ORIENT:  An optional scalar float specifying the orientation of the 
;        violin plots.  The is the angle from the vertical in which increasing 
;        values of PDF_LOC point.  If input then X_POS and YPOS must both be in 
;        input.
;    PDF_AMP:  A required float array of size N_LOC,N_PDF specifying the 
;        amplitudes of the N_PDF density functions at the N_LOC locations.  
;        The amplitudes are plotted in the direction 90 degrees either side of 
;        PDF_LOC.
;    PDF_LOC:  A required float vector of length N_LOC specifying the locations 
;        at which to plot the density function amplitudies.  If ORIENT is 
;        input, later elements are located in the direction of ORIENT in 
;        relation to earlier elements.  Otherwise this refers to locations 
;        in the vertical if only X_POS is input, or in the horizontal if only 
;        Y_POS is input.
;    PSYM:  An optional scalar integer specifying the plotting symbol to use, 
;        according to IDL's plot procedure PSYM input.  The default is none.
;    THICK:  An optional scalar integer specifying the thickness of the plotted 
;        lines when FILL=0.  The default is 1..
;    X_POS:  An optional float vector of length N_PDF indicating the position 
;        of the N_PDF violin plots along the horizontal axis.  If input and 
;        Y_POS is not input, then PDF_LOC points in the direction of the 
;        vertical axis.  If all of X_POS, Y_POS, and ORIENT are input then the 
;        X_POS and Y_POS values are the reference points from which PDF_LOC 
;        points in the direction of ORIENT.  Either X_POS only, Y_POS only, or 
;        X_POS and Y_POS and ORIENT together must be input.
;    Y_POS:  An optional float vector of length N_PDF indicating the position 
;        of the N_PDF violin plots along the vertical axis.  If input and X_POS 
;        is not input, then PDF_LOC points in the direction of the vertical 
;        axis.  If all of X_POS, Y_POS, and ORIENT are input then the X_POS and 
;        Y_POS values are the reference points from which PDF_LOC points in the 
;        direction of ORIENT.  Either X_POS only, Y_POS only, or X_POS and 
;        Y_POS and ORIENT together must be input.
;
; USES:
;    -
;
; PROCEDURE:
;    This procedure plots lines or shapes forming the "violin plot" method of 
;    plotting density functions.
;
; EXAMPLE:
;    ; Generate two density functions, the opposite of each other.
;    x_pos = [ 2, 4 ]
;    y_pos = [ 1, 2.5 ]
;    pdf_loc = [ 0., 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4 ]
;    pdf_amp = [ [ 0.1, 0.3,  0.8,  0.5,  0.3, 0.1, 0.1, 0.0 ], $
;        [ 0.0, 0.1, 0.1, 0.3, 0.5, 0.8, 0.3, 0.1 ] ]
;    ; Plot these pointing toward the top
;    plot, [0,5], [0,4], nodata=1, xstyle=1, ystyle=1, isotropic=1
;    violin_plot, x_pos=x_pos, pdf_loc=pdf_loc, pdf_amp=pdf_amp, fill=1
;    ; Plot these pointing toward the right
;    plot, [0,5], [0,4], nodata=1, xstyle=1, ystyle=1, isotropic=1
;    violin_plot, y_pos=y_pos, pdf_loc=pdf_loc, pdf_amp=pdf_amp, fill=1
;    ; Plot these pointing toward the top left
;    plot, [0,5], [0,4], nodata=1, xstyle=1, ystyle=1, isotropic=1
;    violin_plot, x_pos=x_pos, y_pos=y_pos, pdf_loc=pdf_loc, pdf_amp=pdf_amp, $
;        fill=1, orient=45.
;
; MODIFICATION HISTORY:
;    Written by:     Daithi A. Stone, (dastone@runbox.com) 2026-09-10
;-

;***********************************************************************

PRO VIOLIN_PLOT, $
    X_POS=x_pos, Y_POS=y_pos, ORIENT=orient, $
    PDF_LOC=pdf_loc, PDF_AMP=pdf_amp, $
    COLOR=color, $
    PSYM=psym, $
    THICK=thick, $
    FILL=fill_opt

;***********************************************************************
; Constants and options

; The number of PDFs to plot
if not( keyword_set( pdf_amp ) ) then stop
n_pdf = n_elements( pdf_amp[0,*] )

; If the orientation angle has been input
if n_elements( orient ) eq 1 then begin
  ; Ensure reference positions are specified
  if n_elements( x_pos ) ne n_pdf then stop
  if n_elements( y_pos ) ne n_pdf then stop
  ; Copy the orientation
  orient_use = orient
; If the orientation is defined by the coordinate input
endif else begin
  ; Option to plot amplitudes horizontally
  if n_elements( x_pos ) eq n_pdf then begin
    orient_use = -90
    if n_elementS( y_pos ) ne 0 then stop
  ; Option to plot amplitudes vertically
  endif else if n_elements( y_pos ) eq n_pdf then begin
    orient_use = 0
  ; Unsupported orientation
  endif else begin
    stop
  endelse
endelse

; The number of locations in the PDF
n_loc = n_elements( pdf_loc )
if n_loc eq 0 then stop
if n_elements( pdf_amp[*,0] ) ne n_loc then stop

; The default fill option (none)
if not( keyword_set( fill_opt ) ) then fill_opt = 0

;***********************************************************************
; Plot violins

; Iterate through PDFs
for i_pdf = 0, n_pdf - 1 do begin
  ; If plotting with a tilted orientation
  if n_elements( orient ) eq 1 then begin
    ; Calculate the cosine and sine of the angle
    ang_cos = cos( orient_use / 180. * !pi )
    ang_sin = sin( orient_use / 180. * !pi )
    ; Determine the lines to plots
    temp_x = x_pos[i_pdf] $
        + [ ang_cos * pdf_amp[*,i_pdf] - ang_sin * pdf_loc, $
        reverse( -ang_cos * pdf_amp[*,i_pdf] - ang_sin * pdf_loc ) ]
    temp_y = y_pos[i_pdf] $
        + [ ang_cos * pdf_loc + ang_sin * pdf_amp[*,i_pdf], $
        reverse( ang_cos * pdf_loc - ang_sin * pdf_amp[*,i_pdf] ) ]
  ; If plotting amplitudes horizontally
  endif else if orient_use eq -90 then begin
    ; Set the lines to plot
    temp_x = x_pos[i_pdf] + [ pdf_amp[*,i_pdf], reverse( -pdf_amp[*,i_pdf] ) ]
    temp_y = [ pdf_loc, reverse( pdf_loc ) ]
  ; If plotting amplitudes vertically
  endif else if orient_use eq 0 then begin
    temp_x = [ pdf_loc, reverse( pdf_loc ) ]
    temp_y = y_pos[i_pdf] + [ pdf_amp[*,i_pdf], reverse( -pdf_amp[*,i_pdf] ) ]
  endif
  ; If plotting outlines
  if fill_opt eq 0 then begin
    plots, [temp_x[2*n_loc-1],temp_x], [temp_y[2*n_loc-1],temp_y], $
        color=color, thick=thick, psym=psym
  ; If plotting filled violins
  endif else begin
    polyfill, [temp_x[2*n_loc-1],temp_x], [temp_y[2*n_loc-1],temp_y], $
       color=color
  endelse
endfor

;***********************************************************************
; The end

return
END
