// TITLE     : legends_multicolumn() 
//             for graphical axes with many curves and related legends
// AUTHOR (C): Samuel GOUGEON - Le Mans (France) - sgougeon@free.fr
// LICENSE   : CeCILL-C
//   http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html
// SOFTWARE  : for Scilab > 5.0  http://www.scilab.org/
// DEPENDENCY: none
// RELEASE   : 1.0 (2013-02-25)
// DISCLAIMER: No warranty of any kind is provided. Use "as is"
// HELP      : run legends_multicolumn() without input arguments,
//             to display the help and examples for demos.
// HISTORY   : 
//   2013-02-25 : First release
// ----------------------------
function legends_multicolumn(Texts, Styles, Lpc, font_size, thickness, pos, with_box)
// For many curves & related legends, displays a multicolumn bloc of legends:
//  - The shape of the bloc can be specified (Lpc)
//  - Positionning with Logarithmic or/and reversed axes is supported
//  - Lines styles and Markers styles are supported and can be mixed
//    in the Styles vector, but without overlaying
//  - Lines thickness(es) can be specified
//
// Texts  : vector of legends
// Styles : vector of related lines or markers styles
//          Styles(i)>0 -> line color   -14<= Styles(i) <=-1 -> marker
// Lpc>0 :  (maximal) number of Lines Per Column
// Lpc<0 : -(maximal) number of Columns per line
// with_box : boolean: if %T, draws the global box of legends (default)
// pos : position of the block:
//      "ur" | 1 : in the upper right corner (default)
//      "ul" | 2 : in the upper left corner
//      "ll" | 3 : in the lower left corner
//      "lr" | 4 : in the lower right corner
//      "?"  | 5 : interactive positionning with the mouse
//      [xr,yr]: relative coordinates 0 <= xr,yr <= 1 of the upper left 
//         corner of the block, with respect to the upper left corner
//         of the data bounds area. [0,0] is equivalent to "ul"
// thickness: scalar or vector of lines thickness. If a vector is
//    provided, its length must = Styles one
// 
// EXAMPLES: run legends_multicolumn(..) with only 0 to 2 parameters:
//   legends_multicolumn()
//   legends_multicolumn( with_box=%f )
//   legends_multicolumn( Lpc=8, pos="?" )
//   legends_multicolumn( Lpc=-3, pos="lr" )
//   legends_multicolumn( font_size=2, pos="ll" )
//   legends_multicolumn( pos="?", thickness=2 )
//   legends_multicolumn( pos="lr", thickness=1+round(rand(1:19)) )
//   etc

    ax = gca();
    if ~isdef("font_size") then
        font_size = ax.font_size
    end
    if ~isdef("thickness") then
        thickness = ax.thickness
    end
    if ~isdef("with_box") then
        with_box = %t
    end
    if ~isdef("pos") then
        pos = "ur"
    end
    
    // EXAMPLES = DEMOS
    if argn(2)<3 then
        head_comments("legends_multicolumn")
        clf
        x = logspace(-2,2,100)
        plot2d("ll",x,exp(x).*(1.01+sin(x)))
        ax = gca()
        ax.axes_reverse(1:2) = "on"
        nL = 19;   // number of Legends
        legendes = "legend#"+string(1:nL)  // Text of legends
        s = [-14:-1 1:max(14,nL-14)]    // building lines or markers styles
        rand("seed",getdate("s"))
        [tmp, k] = gsort(rand(s), "g")
        s = s(k(1:nL))
        if ~isdef("Lpc"), Lpc = -2, end
        legends_multicolumn(legendes, s, Lpc, font_size=font_size, pos=pos, thickness=thickness, with_box=with_box);
        return
    end
    // ----------------------------------------------------------
    nL = size(Texts,"*")
    if Lpc<0 then
        Lpc = ceil(-nL/Lpc)
    end
    x = 0
    i = 1
    j = min([Lpc, nL])
    ymin = %inf
    ymax = -%inf
    L = []  // vector of handles of colums
    drawlater()
    while i<=nL
        legends(Texts(i:j), Styles(i:j), font_size=font_size, opt="ul")
        idL = gce()
        move(idL, [x,0])
        L = [idL L]
        frame = idL.children($)
        if i==1, Frame = frame; end // for the global frame
        w = strange(frame.data(:,1)) // width of this block
        ymin = min([ymin ; frame.data(:,2)])
        ymax = max([ymax ; frame.data(:,2)])
        if ~with_box | i>1  // masks the frame
            idL.children($).line_mode = "off"
        end

        // Set the thickess of line style
        if isdef("thickness") then
            if length(thickness)==1 then
                idL.children(2:2:$).thickness = thickness
            else
                for k = i:j
                    idL.children(2*(k-i+1)).thickness = thickness(k)
                end
            end
        end
        
        x = x + w    // updates the position of next block
        i = i + Lpc
        j = min([j+Lpc, nL])
    end
    
    // GLOBAL BOX
    W = (x + w)*1.025   // Total width
    H = ymax - ymin 
    dh = (ymax - ymin)/70
    H = H + 2*dh
    if with_box then
        Frame.data([2 3],1) = W
        Frame.data([1 2],2) = ymax + dh
        Frame.data([3 4],2) = ymin - dh
        Frame.foreground = color("grey")
        //Frame.fill_mode = "off"
    end
    
    // POSITIONNING
    // ------------
    if isdef("pos") then
        db = ax.data_bounds   // db(1,:)=mins  db(2,:)=maxs
        m = ax.margins
        // Horizontal shift to the right
        if pos=="ur" | pos==1 | pos=="lr" | pos==4 then
            s = 0.995 - m(2)- W
            for i = 1:length(L)
                move(L(i), [s,0])
            end
        end
        // Vertical shift to the bottom
        if pos=="ll" | pos==3 | pos=="lr" | pos==4
            s = (0.98 - m(4)) - (m(3)+H)
            for i = 1:length(L)
                move(L(i), [0,-s])
            end
        end
        // Explicit [x,y] position (relative coordinates in the axes,
        //  w.r.t the upper left corner of the axes):
        if typeof(pos)=="constant" & length(pos)==2 then
            w = 1 - sum(m(1:2))
            s = w * pos(1)
            for i = 1:length(L)
                move(L(i), [s,0])
            end
            // vertically:
            h = 1 - sum(m(3:4))
            s = h * pos(2)
            for i = 1:length(L)
                move(L(i), [0,-s])
            end
            
        end
        // Interactive positionning
        if pos=="?" | pos==5 then
            axm = gca()
            tmp = Frame, while tmp.type~="Axes", tmp = tmp.parent, end
            sca(tmp)
            drawnow()
            d = Frame.data
            r0 = [min(d(:,1)) max(d(:,2)) strange(d(:,1)) strange(d(:,2)) ]
            r = dragrect(r0)
            drawlater()
            for i = 1:length(L)
                move(L(i), [r(1)-r0(1) r(2)-r0(2)])
            end
            sca(axm)
        end
    end
    drawnow()
endfunction
