view yetilab/plot/plot.yeti @ 136:1101ecb57e47

Backed out changeset b1968c825a53 Horizontal bar chart doesn't work any better for us than vertical, and this polygon structure doesn't seem to render as cleanly
author Chris Cannam
date Tue, 23 Apr 2013 13:51:12 +0100
parents b1968c825a53
children b28512329efc
line wrap: on
line source
module yetilab.plot.plot;

import org.jzy3d.plot3d.builder: Mapper;
import org.jzy3d.plot3d.text.drawable: DrawableTextBillboard, DrawableTextBitmap;
import org.jzy3d.maths: Range, Coord3d;
import org.jzy3d.plot3d.primitives: Shape, HistogramBar, FlatLine2d, Polygon, Quad, Point;
import org.jzy3d.plot3d.primitives.axes.layout.providers: StaticTickProvider;
import org.jzy3d.plot3d.primitives.axes.layout.renderers: ITickRenderer, TickLabelMap;
import org.jzy3d.chart: Chart, ChartLauncher;
import org.jzy3d.plot3d.builder: Builder;
import org.jzy3d.plot3d.builder.concrete: OrthonormalGrid;
import org.jzy3d.colors.colormaps: ColorMapRainbow;
import org.jzy3d.colors: ColorMapper, Color;
import org.jzy3d.plot3d.rendering.canvas: Quality;
import org.jzy3d.plot3d.rendering.view.modes: ViewPositionMode;

chartColours = array [
    { r = 82,  g = 126, b = 154 }, // dark steel blue
    { r = 161, g = 54,  b = 2   }, // red
    { r = 207, g = 228, b = 148 }, // grey-green
    { r = 21,  g = 183, b = 197 }, // light blue
    { r = 251, g = 116, b = 43  }, // light red
    { r = 200, g = 125, b = 234 }, // light purple
    { r = 126, g = 33,  b = 28  }, // dried blood!
    { r = 188, g = 13,  b = 207 }, // mid purple
];    

chartColour n =
    if n < 0
    then chartColour (-n)
    else
        rgb = chartColours[n % (length chartColours)];
        new Color(rgb.r / 255.0, rgb.g / 255.0, rgb.b / 255.0);
    fi;

newMatrixMapper matrix =
   (class MMapper extends Mapper
        double f(double x, double y)
            result = matrix.getAt y x;
            println "f(\(x),\(y)) -> \(result)";
            result
    end;
    new MMapper());

newMatrixLogMapper matrix =
   (class MMapper extends Mapper
        double f(double x, double y)
            ln (matrix.getAt y x)
    end;
    new MMapper());

newMapper mapFunction =
   (class FMapper extends Mapper
        double f(double x, double y)
            mapFunction x y
    end;
    new FMapper());

newPercentTickRenderer () =
   (f v = " \(int (v * 100))%";
    class PercentageTickRenderer extends ITickRenderer
        String format(double value) f value,
        String format(float value) f value
    end;
    new PercentageTickRenderer());

parseOptions options defaultKeys defaultXKeys =
   (parsed = {
        var keys = array (sort defaultKeys),
        var labels = [:],
        var animated = false,
        var normalised = false,
        var unit = "",
        var xkeys = array (sort defaultXKeys)
    };
    for options
       \case of
        Keys kk: parsed.keys := array kk;
        XKeys xk: parsed.xkeys := array xk;
        Animated a: parsed.animated := a;
        Normalised n: parsed.normalised := n;
        Unit u: parsed.unit := u;
        Labels ll: parsed.labels := ll;
        esac;
    if empty? parsed.labels then
        parsed.labels := mapIntoHash id id parsed.keys
    fi;
    parsed);

plotMatrix matrix =
   (mapper = newMatrixMapper matrix;
    size = matrix.size;
    xrange = new Range(0, size.columns - 1);
    yrange = new Range(0, size.rows - 1);
    grid = new OrthonormalGrid(xrange, size.columns, yrange, size.rows);
    println "Matrix size: \(size)";
    surface = Builder#buildOrthonormal(grid, mapper); //??? big?
    println "Z Bounds: \(surface#getBounds()#getZmin()) -> \(surface#getBounds()#getZmax())";
    surface#setFaceDisplayed(true);
    surface#setWireframeDisplayed(true);
    surface#setWireframeColor(Color#BLACK);
//    chart = new Chart(Quality#Fastest, "swing");
    chart = new Chart(Quality#Nicest);
    chart#getScene()#getGraph()#add(surface);
    ChartLauncher#openChart(chart);
    ());

plotBarChart options values =
   (opts = parseOptions options (keys values) [];
    quality = Quality#Fastest;
    quality#setAnimated(opts.animated);
    chart = new Chart(quality);
    var n = length opts.keys;
    scene = chart#getScene();
    ticks = new float[n+1];
    tickLabels = new TickLabelMap();
    var i = 0;
    var x = n - i - 1;
    total = sum (map do k: if k in values then values[k] else 0 fi done opts.keys);
    for opts.keys do k:
        bar = new HistogramBar();
        v = if k in values then values[k] else 0 fi;
        v = if opts.normalised and total > 0 then v / total else v fi;
        bar#setData(new Coord3d(x, 0, 0), v, 0.45, chartColour i);
        bar#setWireframeDisplayed(false);
        scene#add(bar);
        ticks[i] := i;
        tickLabels#register(x, opts.labels[k]);
        i := i + 1;
        x := x - 1;
    done;
    chart#getView()#setViewPoint(new Coord3d(pi/2, 0, 0));
    axes = chart#getAxeLayout();
    axes#setXAxeLabelDisplayed(false);
    axes#setYAxeLabelDisplayed(false);
    axes#setZAxeLabelDisplayed(true);
    if opts.normalised then
        axes#setZAxeLabel("");
        axes#setZTickRenderer(newPercentTickRenderer ());
    else
        axes#setZAxeLabel(opts.unit);
    fi;
    axes#setXTickProvider(new StaticTickProvider(ticks));
    axes#setXTickRenderer(tickLabels);
    axes#setYTickLabelDisplayed(false);
    ChartLauncher#openChart(chart);
    ());

plotLines options values =
   (opts = parseOptions options (keys values) (keys values[head (keys values)]);
    quality = Quality#Fastest;
    quality#setAnimated(opts.animated);
    chart = new Chart(quality);
    scene = chart#getScene();
    n = length opts.xkeys;
    var z = 0;
    for opts.keys do k:
        v = values[k];
        x = new float[n];
        y = new float[n];
        var i = 0;
        for opts.xkeys do xk:
            x[i] := i;
            y[i] := if xk in v then v[xk] else 0 fi;
            i := i + 1;
        done;
        line = new FlatLine2d(x, y, z);
        line#setWireframeDisplayed(true);
        line#setWireframeColor(chartColour z);
        line#setWireframeWidth(2);
        line#setFaceDisplayed(false);
        scene#add(line);
        z := z + 1;
    done;
    chart#getView()#setViewPoint(new Coord3d(0, 0, 0));
    axes = chart#getAxeLayout();
    axes#setXAxeLabelDisplayed(false);
    axes#setYAxeLabelDisplayed(false);
    axes#setZAxeLabelDisplayed(true);
    axes#setZAxeLabel(opts.unit);
    axes#setYTickLabelDisplayed(false);
    ChartLauncher#openChart(chart);
    ());

stack keys xkeys values normalised =
   (stacked = mapIntoHash id \(mapIntoHash id \{ y0 = 0, y1 = 0 } xkeys) keys;
    prev = mapIntoHash id \0 xkeys;
    valueOf k xk = if xk in values[k] then values[k][xk] else 0 fi;
    for xkeys do xk:
        total = sum (map do k: valueOf k xk done keys);
        for keys do k:
            value =
                if normalised and total > 0
                then (valueOf k xk) / total
                else (valueOf k xk)
                fi;
            height = prev[xk] + value;
            stacked[k][xk] := { y0 = prev[xk], y1 = height };
            prev[xk] := height;
        done;
    done;
    stacked);

newRect x y0 y1 z colour is number -> number -> number -> number -> ~Color -> 'a =
   (poly = new Quad();
    poly#add(new Point(new Coord3d(x + 0.5, z, y0)));
    poly#add(new Point(new Coord3d(x + 0.5, z, y1)));
    poly#add(new Point(new Coord3d(x - 0.5, z, y1)));
    poly#add(new Point(new Coord3d(x - 0.5, z, y0)));
    poly#setWireframeDisplayed(true);
    poly#setWireframeColor(colour);
    poly#setFaceDisplayed(true);
    poly#setColor(colour);
    poly);

plotStacked options values =
   (opts = parseOptions options (keys values) (keys values[head (keys values)]);
    quality = Quality#Fastest;
    quality#setAnimated(opts.animated);
    chart = new Chart(quality);
    scene = chart#getScene();
    stacked = stack opts.keys opts.xkeys values opts.normalised;
    var z = 0;
    var ty = 0;
    xtickLabels = new TickLabelMap();
    for [0..length opts.xkeys - 1] do x:
        xtickLabels#register(x, opts.xkeys[x]);
    done;
    for opts.keys do k:
        ranges = stacked[k];
        c = chartColour z;
        for [0..length opts.xkeys - 1] do x:
            xk = opts.xkeys[x];
            rect = newRect x ranges[xk].y0 ranges[xk].y1 z c;
            scene#add(rect);
        done;
        text = new DrawableTextBitmap(opts.labels[k], new Coord3d(-5, z, ty), c);
        scene#add(text);
        z := z - 1;
        ty := ty + 0.1;
    done;
    chart#getView()#setViewPoint(new Coord3d(-pi/2, 0, 0));
    axes = chart#getAxeLayout();
    axes#setXAxeLabelDisplayed(false);
    axes#setXTickRenderer(xtickLabels);
    axes#setYAxeLabelDisplayed(false);
    axes#setZAxeLabelDisplayed(true);
    if opts.normalised then 
        axes#setZAxeLabel("");
        axes#setZTickRenderer(newPercentTickRenderer ());
    else
        axes#setZAxeLabel(opts.unit);
    fi;
    axes#setYTickLabelDisplayed(false);
    ChartLauncher#openChart(chart);
    ());

plotStructure structure =
    case structure of
    Grid matrix:
        plotMatrix matrix;
    //!!!
    _: failWith "Cannot plot this structure (only grids implemented so far)";
    esac;

{
    newMatrixMapper,
    newMatrixLogMapper,
    newMapper,
    plotMatrix, 
    plotBarChart,
    plotLines,
    stack,
    plotStacked,
    plotStructure,
}