; $Id: dendro_plot.pro,v 1.6 2004/03/22 16:13:15 chris Exp $
; Copyright (c) 2003-2004, Research Systems, Inc.  All rights reserved.
;       Unauthorized reproduction prohibited.
;+
; NAME:
;   DENDRO_PLOT
;
; PURPOSE:
;   Given a hierarchical tree cluster, as created by CLUSTER_TREE,
;   the DENDRO_PLOT procedure draws a two-dimensional dendrite plot
;   on the current direct graphics device.
;
; CALLING SEQUENCE:
;   DENDRO_PLOT, Clusters, Linkdistance
;
; INPUTS:
;   Clusters: A 2-by-(m-1) input array containing the cluster indices,
;       where m is the number of items in the original dataset.
;       This array is usually the result of the CLUSTER_TREE function.
;
;   Linkdistance: An (m-1)-element input vector containing the distances
;       between cluster items, as returned by the Linkdistance argument
;       to the CLUSTER_TREE function.
;
; KEYWORD PARAMETERS:
;   See the IDL Reference Manual for a description of the keywords.
;
; EXAMPLE:
;    ; Given a set of points in two-dimensional space.
;    m = 20
;    data = 7*RANDOMN(-1, 2, m)
;
;    ; Compute the Euclidean distance between each point.
;    distance = DISTANCE_MEASURE(data)
;
;    ; Compute the cluster analysis.
;    clusters = CLUSTER_TREE(distance, linkdistance, LINKAGE=2)
;
;    DENDRO_PLOT, clusters, linkdistance, $
;        POSITION=[0.08, 0.1, 0.48, 0.9], $
;        XSTYLE=9, YSTYLE=9, $
;        XTITLE='Leaf', YTITLE='Distance'
;
;    DENDRO_PLOT, clusters, linkdistance, $
;        ORIENTATION=1, /NOERASE, $
;        POSITION=[0.56, 0.1, 0.96, 0.9], $
;        XSTYLE=9, YSTYLE=9, $
;        XTITLE='Distance', YTITLE='Leaf'
;
; MODIFICATION HISTORY:
;   Written by:  CT, RSI, Sept 2003
;   Modified:
;
;-


;-------------------------------------------------------------------------
pro dendro_plot, clusters, linkdistance, $
    CHARSIZE=charsizeIn, $
    CHARTHICK=charthick, $
    COLOR=colorIn, $
    FONT=font, $
    LABEL_CHARSIZE=labelCharsizeIn, $
    LABEL_CHARTHICK=labelCharthickIn, $
    LABEL_COLOR=labelColorIn, $
    LABEL_NAMES=labelNamesIn, $
    LABEL_ORIENTATION=labelOrientIn, $
    LINECOLOR=lineColorIn, $
    LINESTYLE=lineStyle, $
    ORIENTATION=orientationIn, $
    OVERPLOT=overplot, $
    THICK=lineThick, $
    XRANGE=xrangeIn, YRANGE=yrangeIn, $
    XSTYLE=xstyleIn, YSTYLE=ystyleIn, $
    XTICKLEN=xticklenIn, YTICKLEN=yticklenIn, $
    XTICKS=xticksIn, YTICKS=yticksIn, $
    XTICKV=xtickvIn, YTICKV=ytickvIn, $
    XTICKNAME=xticknameIn, YTICKNAME=yticknameIn, $
    XTITLE=xtitleIn, YTITLE=ytitleIn, $
    _REF_EXTRA=_extra


    compile_opt idl2

    ON_ERROR, 2

    if (N_PARAMS() ne 2) then $
        MESSAGE, 'Incorrect number of arguments.'

    ; Retrieve the vertex and connectivity for the dendrogram.
    DENDROGRAM, clusters, linkdistance, outverts, outconn, $
        LEAFNODES=leafnodes

    dimC = SIZE(clusters, /DIMENSIONS)
    m = dimC[1] + 1

    if (N_ELEMENTS(labelNamesIn) eq m) then begin
        ; Rearrange labels to match the new leaf node positions.
        labelNames = labelNamesIn[leafnodes]
    endif else begin
        if (N_ELEMENTS(labelNamesIn) ne 1) then $
            labelNames = STRTRIM(leafnodes, 2)
    endelse

    color = (N_ELEMENTS(colorIn) gt 0) ? colorIn : !P.color
    charsize = N_ELEMENTS(charsizeIn) ? charsizeIn : $
        (!P.CHARSIZE ne 0 ? !P.CHARSIZE : 1)
    lineColor = (N_ELEMENTS(lineColorIn) gt 0) ? lineColorIn : color


    orientation = (N_ELEMENTS(orientationIn) eq 1) ? orientationIn : 0

    xrange = (N_ELEMENTS(xrangeIn) eq 2) ? xrangeIn : [-1, m]
    maxx = MAX(outverts[1,*], MIN=minn)
    yrange = (N_ELEMENTS(yrangeIn) eq 2) ? yrangeIn : $
        [minn, maxx + 0.1*(maxx - minn)]


    if (orientation lt 0) || (orientation gt 3) || $
        (orientation ne FIX(orientation)) then $
        MESSAGE, 'Illegal keyword value for ORIENTATION.'

    if ((orientation eq 0) || (orientation eq 2)) then begin
        xx = REFORM(outverts[0,*], 4, m-1)
        yy = REFORM(outverts[1,*], 4, m-1)
        xticklen = 1e-5
        xticks = 1
        xtickname = [' ',' ']   ; suppress
        if (orientation eq 2) then $
            yrange = yrange[[1,0]]
        ; Allow these keywords for the Y axis.
        if (N_ELEMENTS(ytitleIn) eq 1) then $
            ytitle = ytitleIn
        if (N_ELEMENTS(yticklenIn) gt 0) then $
            yticklen = yticklenIn
        if (N_ELEMENTS(yticksIn) gt 0) then $
            yticks = yticksIn
        if (N_ELEMENTS(ytickvIn) gt 0) then $
            ytickv = ytickvIn
        if (N_ELEMENTS(yticknameIn) gt 0) then $
            ytickname = yticknameIn
    endif else begin
        xx = REFORM(outverts[1,*], 4, m-1)
        yy = REFORM(outverts[0,*], 4, m-1)
        tmp = xrange
        xrange = (orientation eq 1) ? yrange : yrange[[1,0]]
        yrange = tmp
        yticklen = 1e-5
        yticks = 1
        ytickname = [' ',' ']   ; suppress
        ; Allow these keywords for the X axis.
        if (N_ELEMENTS(xtitleIn) eq 1) then $
            xtitle = xtitleIn
        if (N_ELEMENTS(xticklenIn) gt 0) then $
            xticklen = xticklenIn
        if (N_ELEMENTS(xticksIn) gt 0) then $
            xticks = xticksIn
        if (N_ELEMENTS(xtickvIn) gt 0) then $
            xtickv = xtickvIn
        if (N_ELEMENTS(xticknameIn) gt 0) then $
            xtickname = xticknameIn
    endelse


    if (~KEYWORD_SET(overplot)) then begin
        xstyle = (N_ELEMENTS(xstyleIn) eq 1) ? xstyleIn : 1
        ystyle = (N_ELEMENTS(ystyleIn) eq 1) ? ystyleIn : 1

        PLOT, [0,1], /NODATA, $
            CHARSIZE=charsize, $
            CHARTHICK=charthick, $
            COLOR=color, $
            FONT=font, $
            XSTYLE=xstyle, $
            YSTYLE=ystyle, $
            XTICKLEN=xticklen, YTICKLEN=yticklen, $
            XRANGE=xrange, YRANGE=yrange, $
            XTICKS=xticks, YTICKS=yticks, $
            XTICKV=xtickv, YTICKV=ytickv, $
            XTICKNAME=xtickname, YTICKNAME=ytickname, $
            XTITLE=xtitle, YTITLE=ytitle, $
            _EXTRA=_extra
    endif


    for i=0,m-2 do begin
        PLOTS, xx[*,i], yy[*,i], $
            COLOR=lineColor, $
            LINESTYLE=lineStyle, $
            THICK=lineThick
    endfor


    if (N_ELEMENTS(labelNames) eq m) then begin

        ; Construct the labels. This is complicated because we can't use AXIS
        ; ticks (because there is a limit of 60), and so we need to compute
        ; the positions ourself. We also allow different label angles.

        labelColor = (N_ELEMENTS(labelColorIn) gt 0) ? $
            labelColorIn : color
        labelCharsize = (N_ELEMENTS(labelCharsizeIn) gt 0) ? $
            labelCharsizeIn : charsize
        if (labelCharsize le 0) then $
            labelCharsize = 1
        if (N_ELEMENTS(labelCharthickIn) gt 0) then $
            labelCharthick = labelCharthickIn $
        else if (N_ELEMENTS(charthick) gt 0) then $
            labelCharthick = charthick

        xdevice = labelCharsize*!D.x_ch_size
        char = CONVERT_COORD([[0,0], [xdevice, 0]], /DEVICE, /TO_DATA)
        width = char[0,1] - char[0,0]
        ydevice = labelCharsize*!D.y_ch_size
        char = CONVERT_COORD([[0,0], [0, ydevice]], /DEVICE, /TO_DATA)
        height = char[1,1] - char[1,0]
        char = CONVERT_COORD([[0,0], [ydevice, 0]], /DEVICE, /TO_DATA)
        sideways = char[0,1] - char[0,0]

        if ((orientation eq 0) || (orientation eq 2)) then begin
            xloc = LINDGEN(m)
            yloc = FLTARR(m)
            ; If label orient not provided, rotate by 90 deg if labels
            ; are too long. This is just a guess.
            labelOrient = N_ELEMENTS(labelOrientIn) ? labelOrientIn : $
                ((MAX(STRLEN(labelNames)) le 2) ? 0 : 90)
        endif else begin
            xloc = FLTARR(m)
            yloc = LINDGEN(m)
            labelOrient = N_ELEMENTS(labelOrientIn) ? labelOrientIn : 0
        endelse

        ; Reduce orientation to -180...+180
        labelOrient mod= 360
        if (labelOrient gt 180) then labelOrient -= 360 $
        else if (labelOrient lt -180) then labelOrient += 360
        angle = FLOAT(labelOrient)

        case orientation of

            0: begin      ; leafs at bottom
                if (angle eq 0) then begin
                    labelAlign = 0.5
                    yloc -= 1.5*height
                endif else begin
                    labelAlign = (angle gt 0) ? 1 : 0
                    ; Adjust baseline for pivoting about the bottom.
                    yloc -= (1.4 - ABS(angle)/90)*height
                    xloc += 0.4*sideways*(angle gt 0 ? 1 : -1)
                endelse
               end

            2: begin      ; leafs at top
                labelAlign = (angle eq 0) ? 0.5 : $
                    ((angle gt 0) ? 0 : 1)

                yloc += 0.4*height
                xloc += 0.4*sideways*angle/90 + $
                    0.4*width*(1-ABS(angle)/90)*(angle gt 0 ? -1 : 1)
               end

            1: begin      ; leafs at left
                if (ABS(angle) eq 90) then begin
                    labelAlign = 0.5
                    xloc -= 0.75*width
                    if (angle eq -90) then $
                        xloc -= 0.8*sideways
                endif else begin
                    labelAlign = 1
                    xloc -= width* $
                        (angle eq 0 ? 0.5 : (angle gt 0 ? 0.3 : 1))
                    yloc -= height* $
                        (angle eq 0 ? 0.4 : (angle gt 0 ? 0.1 : 0.5))
                endelse
               end

            3: begin      ; leafs at right
                if (ABS(angle) eq 90) then begin
                    labelAlign = 0.5
                    xloc += 0.75*width
                    if (angle eq 90) then $
                        xloc += 0.8*sideways
                endif else begin
                    labelAlign = 0
                    xloc += width* $
                        (angle eq 0 ? 0.5 : (angle gt 0 ? 1.1 : 0.3))
                    yloc -= height* $
                        (angle eq 0 ? 0.4 : (angle gt 0 ? 0.5 : 0.1))
                endelse
               end

        endcase

        XYOUTS, xloc, yloc, labelNames, $
            ALIGN=labelAlign, $
            CHARSIZE=labelCharsize, $
            CHARTHICK=labelCharthick, $
            COLOR=labelColor, $
            FONT=font, $
            ORIENTATION=labelOrient
    endif

    ; Add the title for the leaf axis.
    if ((orientation eq 0) || (orientation eq 2)) then begin
        if (N_ELEMENTS(xtitleIn) eq 1) then begin
            AXIS, XAXIS=(orientation eq 2), $
                CHARSIZE=charsize, $
                CHARTHICK=charthick, $
                COLOR=color, $
                FONT=font, $
                XSTYLE=xstyle, $
                XTICKLEN=1e-5, $
                XTICKS=1, XTICKNAME = [' ',' '], $
                XTITLE=xtitleIn
        endif
    endif else begin
        if (N_ELEMENTS(ytitleIn) eq 1) then begin
            AXIS, YAXIS=(orientation eq 3), $
                CHARSIZE=charsize, $
                CHARTHICK=charthick, $
                COLOR=color, $
                FONT=font, $
                YSTYLE=ystyle, $
                YTICKLEN=1e-5, $
                YTICKS=1, YTICKNAME = [' ',' '], $
                YTITLE=ytitleIn
        endif
    endelse

end

