annotate yetilab/plot/plot.yeti @ 135:b1968c825a53

Horizontal bar charts. Doesn't really help with long label truncation, as the axe renderer just seems to stick the labels too far right anyway
author Chris Cannam
date Tue, 23 Apr 2013 12:54:43 +0100
parents 194fff15f56f
children 1101ecb57e47
rev   line source
Chris@108 1 module yetilab.plot.plot;
Chris@108 2
Chris@108 3 import org.jzy3d.plot3d.builder: Mapper;
Chris@128 4 import org.jzy3d.plot3d.text.drawable: DrawableTextBillboard, DrawableTextBitmap;
Chris@135 5 import org.jzy3d.plot3d.text.renderers.jogl: JOGLTextRenderer;
Chris@119 6 import org.jzy3d.maths: Range, Coord3d;
Chris@126 7 import org.jzy3d.plot3d.primitives: Shape, HistogramBar, FlatLine2d, Polygon, Quad, Point;
Chris@119 8 import org.jzy3d.plot3d.primitives.axes.layout.providers: StaticTickProvider;
Chris@133 9 import org.jzy3d.plot3d.primitives.axes.layout.renderers: ITickRenderer, TickLabelMap;
Chris@108 10 import org.jzy3d.chart: Chart, ChartLauncher;
Chris@108 11 import org.jzy3d.plot3d.builder: Builder;
Chris@108 12 import org.jzy3d.plot3d.builder.concrete: OrthonormalGrid;
Chris@108 13 import org.jzy3d.colors.colormaps: ColorMapRainbow;
Chris@119 14 import org.jzy3d.colors: ColorMapper, Color;
Chris@108 15 import org.jzy3d.plot3d.rendering.canvas: Quality;
Chris@119 16 import org.jzy3d.plot3d.rendering.view.modes: ViewPositionMode;
Chris@108 17
Chris@132 18 chartColours = array [
Chris@132 19 { r = 82, g = 126, b = 154 }, // dark steel blue
Chris@132 20 { r = 161, g = 54, b = 2 }, // red
Chris@132 21 { r = 207, g = 228, b = 148 }, // grey-green
Chris@132 22 { r = 21, g = 183, b = 197 }, // light blue
Chris@132 23 { r = 251, g = 116, b = 43 }, // light red
Chris@132 24 { r = 200, g = 125, b = 234 }, // light purple
Chris@132 25 { r = 126, g = 33, b = 28 }, // dried blood!
Chris@132 26 { r = 188, g = 13, b = 207 }, // mid purple
Chris@131 27 ];
Chris@131 28
Chris@132 29 chartColour n =
Chris@132 30 if n < 0
Chris@132 31 then chartColour (-n)
Chris@132 32 else
Chris@132 33 rgb = chartColours[n % (length chartColours)];
Chris@132 34 new Color(rgb.r / 255.0, rgb.g / 255.0, rgb.b / 255.0);
Chris@132 35 fi;
Chris@132 36
Chris@108 37 newMatrixMapper matrix =
Chris@108 38 (class MMapper extends Mapper
Chris@108 39 double f(double x, double y)
Chris@117 40 result = matrix.getAt y x;
Chris@117 41 println "f(\(x),\(y)) -> \(result)";
Chris@117 42 result
Chris@108 43 end;
Chris@108 44 new MMapper());
Chris@108 45
Chris@108 46 newMatrixLogMapper matrix =
Chris@108 47 (class MMapper extends Mapper
Chris@108 48 double f(double x, double y)
Chris@108 49 ln (matrix.getAt y x)
Chris@108 50 end;
Chris@108 51 new MMapper());
Chris@108 52
Chris@108 53 newMapper mapFunction =
Chris@108 54 (class FMapper extends Mapper
Chris@108 55 double f(double x, double y)
Chris@108 56 mapFunction x y
Chris@108 57 end;
Chris@108 58 new FMapper());
Chris@108 59
Chris@133 60 newPercentTickRenderer () =
Chris@133 61 (f v = " \(int (v * 100))%";
Chris@133 62 class PercentageTickRenderer extends ITickRenderer
Chris@133 63 String format(double value) f value,
Chris@133 64 String format(float value) f value
Chris@133 65 end;
Chris@133 66 new PercentageTickRenderer());
Chris@133 67
Chris@132 68 parseOptions options defaultKeys defaultXKeys =
Chris@132 69 (parsed = {
Chris@133 70 var keys = array (sort defaultKeys),
Chris@132 71 var labels = [:],
Chris@132 72 var animated = false,
Chris@132 73 var normalised = false,
Chris@132 74 var unit = "",
Chris@135 75 var xkeys = array (sort defaultXKeys),
Chris@135 76 var horizontal = false,
Chris@132 77 };
Chris@132 78 for options
Chris@132 79 \case of
Chris@133 80 Keys kk: parsed.keys := array kk;
Chris@133 81 XKeys xk: parsed.xkeys := array xk;
Chris@132 82 Animated a: parsed.animated := a;
Chris@132 83 Normalised n: parsed.normalised := n;
Chris@132 84 Unit u: parsed.unit := u;
Chris@132 85 Labels ll: parsed.labels := ll;
Chris@135 86 Horizontal h: parsed.horizontal := h;
Chris@132 87 esac;
Chris@132 88 if empty? parsed.labels then
Chris@132 89 parsed.labels := mapIntoHash id id parsed.keys
Chris@132 90 fi;
Chris@132 91 parsed);
Chris@132 92
Chris@115 93 plotMatrix matrix =
Chris@117 94 (mapper = newMatrixMapper matrix;
Chris@108 95 size = matrix.size;
Chris@119 96 xrange = new Range(0, size.columns - 1);
Chris@119 97 yrange = new Range(0, size.rows - 1);
Chris@108 98 grid = new OrthonormalGrid(xrange, size.columns, yrange, size.rows);
Chris@112 99 println "Matrix size: \(size)";
Chris@117 100 surface = Builder#buildOrthonormal(grid, mapper); //??? big?
Chris@117 101 println "Z Bounds: \(surface#getBounds()#getZmin()) -> \(surface#getBounds()#getZmax())";
Chris@121 102 surface#setFaceDisplayed(true);
Chris@108 103 surface#setWireframeDisplayed(true);
Chris@108 104 surface#setWireframeColor(Color#BLACK);
Chris@112 105 // chart = new Chart(Quality#Fastest, "swing");
Chris@112 106 chart = new Chart(Quality#Nicest);
Chris@108 107 chart#getScene()#getGraph()#add(surface);
Chris@115 108 ChartLauncher#openChart(chart);
Chris@115 109 ());
Chris@108 110
Chris@135 111 newRect x y0 y1 z colour is number -> number -> number -> number -> ~Color -> 'a =
Chris@135 112 (poly = new Quad();
Chris@135 113 poly#add(new Point(new Coord3d(x + 0.5, z, y0)));
Chris@135 114 poly#add(new Point(new Coord3d(x + 0.5, z, y1)));
Chris@135 115 poly#add(new Point(new Coord3d(x - 0.5, z, y1)));
Chris@135 116 poly#add(new Point(new Coord3d(x - 0.5, z, y0)));
Chris@135 117 poly#setWireframeDisplayed(true);
Chris@135 118 poly#setWireframeColor(colour);
Chris@135 119 poly#setFaceDisplayed(true);
Chris@135 120 poly#setColor(colour);
Chris@135 121 poly);
Chris@135 122
Chris@135 123 newProjectedRect x y0 y1 z colour is number -> number -> number -> number -> ~Color -> 'a =
Chris@135 124 (poly = new Quad();
Chris@135 125 poly#add(new Point(new Coord3d(x + 0.5, y0, z)));
Chris@135 126 poly#add(new Point(new Coord3d(x + 0.5, y1, z)));
Chris@135 127 poly#add(new Point(new Coord3d(x - 0.5, y1, z)));
Chris@135 128 poly#add(new Point(new Coord3d(x - 0.5, y0, z)));
Chris@135 129 poly#setWireframeDisplayed(true);
Chris@135 130 poly#setWireframeColor(colour);
Chris@135 131 poly#setFaceDisplayed(true);
Chris@135 132 poly#setColor(colour);
Chris@135 133 poly);
Chris@135 134
Chris@132 135 plotBarChart options values =
Chris@132 136 (opts = parseOptions options (keys values) [];
Chris@132 137 quality = Quality#Fastest;
Chris@132 138 quality#setAnimated(opts.animated);
Chris@131 139 chart = new Chart(quality);
Chris@132 140 var n = length opts.keys;
Chris@119 141 scene = chart#getScene();
Chris@132 142 ticks = new float[n+1];
Chris@119 143 tickLabels = new TickLabelMap();
Chris@132 144 var i = 0;
Chris@132 145 var x = n - i - 1;
Chris@134 146 total = sum (map do k: if k in values then values[k] else 0 fi done opts.keys);
Chris@132 147 for opts.keys do k:
Chris@134 148 v = if k in values then values[k] else 0 fi;
Chris@133 149 v = if opts.normalised and total > 0 then v / total else v fi;
Chris@135 150 c = chartColour i;
Chris@135 151 scene#add(newRect x 0 v 1 c);
Chris@135 152 scene#add(newProjectedRect x 0 v 1 c);
Chris@132 153 ticks[i] := i;
Chris@132 154 tickLabels#register(x, opts.labels[k]);
Chris@132 155 i := i + 1;
Chris@132 156 x := x - 1;
Chris@119 157 done;
Chris@135 158 if opts.horizontal then
Chris@135 159 chart#getView()#setViewPoint(new Coord3d(0, pi/2, 0));
Chris@135 160 else
Chris@135 161 chart#getView()#setViewPoint(new Coord3d(pi/2, 0, 0));
Chris@135 162 fi;
Chris@135 163
Chris@119 164 axes = chart#getAxeLayout();
Chris@135 165 if opts.horizontal then
Chris@135 166 axes#setZAxeLabelDisplayed(false);
Chris@135 167 axes#setZTickLabelDisplayed(false);
Chris@135 168 axes#setYAxeLabelDisplayed(true);
Chris@135 169 if opts.normalised then
Chris@135 170 axes#setYAxeLabel("");
Chris@135 171 axes#setYTickRenderer(newPercentTickRenderer ());
Chris@135 172 else
Chris@135 173 axes#setYAxeLabel(opts.unit);
Chris@135 174 fi;
Chris@135 175 else
Chris@135 176 axes#setYAxeLabelDisplayed(false);
Chris@135 177 axes#setYTickLabelDisplayed(false);
Chris@135 178 axes#setZAxeLabelDisplayed(true);
Chris@135 179 if opts.normalised then
Chris@135 180 axes#setZAxeLabel("");
Chris@135 181 axes#setZTickRenderer(newPercentTickRenderer ());
Chris@135 182 else
Chris@135 183 axes#setZAxeLabel(opts.unit);
Chris@135 184 fi;
Chris@135 185 fi;
Chris@135 186
Chris@119 187 axes#setXAxeLabelDisplayed(false);
Chris@119 188 axes#setXTickProvider(new StaticTickProvider(ticks));
Chris@119 189 axes#setXTickRenderer(tickLabels);
Chris@135 190
Chris@119 191 ChartLauncher#openChart(chart);
Chris@119 192 ());
Chris@119 193
Chris@132 194 plotLines options values =
Chris@132 195 (opts = parseOptions options (keys values) (keys values[head (keys values)]);
Chris@132 196 quality = Quality#Fastest;
Chris@132 197 quality#setAnimated(opts.animated);
Chris@131 198 chart = new Chart(quality);
Chris@124 199 scene = chart#getScene();
Chris@132 200 n = length opts.xkeys;
Chris@124 201 var z = 0;
Chris@132 202 for opts.keys do k:
Chris@124 203 v = values[k];
Chris@124 204 x = new float[n];
Chris@124 205 y = new float[n];
Chris@124 206 var i = 0;
Chris@132 207 for opts.xkeys do xk:
Chris@124 208 x[i] := i;
Chris@124 209 y[i] := if xk in v then v[xk] else 0 fi;
Chris@124 210 i := i + 1;
Chris@124 211 done;
Chris@124 212 line = new FlatLine2d(x, y, z);
Chris@124 213 line#setWireframeDisplayed(true);
Chris@132 214 line#setWireframeColor(chartColour z);
Chris@124 215 line#setWireframeWidth(2);
Chris@124 216 line#setFaceDisplayed(false);
Chris@124 217 scene#add(line);
Chris@124 218 z := z + 1;
Chris@124 219 done;
Chris@124 220 chart#getView()#setViewPoint(new Coord3d(0, 0, 0));
Chris@124 221 axes = chart#getAxeLayout();
Chris@124 222 axes#setXAxeLabelDisplayed(false);
Chris@124 223 axes#setYAxeLabelDisplayed(false);
Chris@124 224 axes#setZAxeLabelDisplayed(true);
Chris@132 225 axes#setZAxeLabel(opts.unit);
Chris@124 226 axes#setYTickLabelDisplayed(false);
Chris@124 227 ChartLauncher#openChart(chart);
Chris@124 228 ());
Chris@124 229
Chris@129 230 stack keys xkeys values normalised =
Chris@127 231 (stacked = mapIntoHash id \(mapIntoHash id \{ y0 = 0, y1 = 0 } xkeys) keys;
Chris@125 232 prev = mapIntoHash id \0 xkeys;
Chris@129 233 valueOf k xk = if xk in values[k] then values[k][xk] else 0 fi;
Chris@125 234 for xkeys do xk:
Chris@129 235 total = sum (map do k: valueOf k xk done keys);
Chris@125 236 for keys do k:
Chris@129 237 value =
Chris@129 238 if normalised and total > 0
Chris@129 239 then (valueOf k xk) / total
Chris@129 240 else (valueOf k xk)
Chris@129 241 fi;
Chris@125 242 height = prev[xk] + value;
Chris@127 243 stacked[k][xk] := { y0 = prev[xk], y1 = height };
Chris@125 244 prev[xk] := height;
Chris@125 245 done;
Chris@125 246 done;
Chris@125 247 stacked);
Chris@125 248
Chris@132 249 plotStacked options values =
Chris@132 250 (opts = parseOptions options (keys values) (keys values[head (keys values)]);
Chris@132 251 quality = Quality#Fastest;
Chris@132 252 quality#setAnimated(opts.animated);
Chris@128 253 chart = new Chart(quality);
Chris@125 254 scene = chart#getScene();
Chris@132 255 stacked = stack opts.keys opts.xkeys values opts.normalised;
Chris@125 256 var z = 0;
Chris@130 257 var ty = 0;
Chris@133 258 xtickLabels = new TickLabelMap();
Chris@133 259 for [0..length opts.xkeys - 1] do x:
Chris@133 260 xtickLabels#register(x, opts.xkeys[x]);
Chris@133 261 done;
Chris@132 262 for opts.keys do k:
Chris@127 263 ranges = stacked[k];
Chris@132 264 c = chartColour z;
Chris@133 265 for [0..length opts.xkeys - 1] do x:
Chris@133 266 xk = opts.xkeys[x];
Chris@127 267 rect = newRect x ranges[xk].y0 ranges[xk].y1 z c;
Chris@127 268 scene#add(rect);
Chris@125 269 done;
Chris@132 270 text = new DrawableTextBitmap(opts.labels[k], new Coord3d(-5, z, ty), c);
Chris@128 271 scene#add(text);
Chris@126 272 z := z - 1;
Chris@130 273 ty := ty + 0.1;
Chris@125 274 done;
Chris@129 275 chart#getView()#setViewPoint(new Coord3d(-pi/2, 0, 0));
Chris@125 276 axes = chart#getAxeLayout();
Chris@125 277 axes#setXAxeLabelDisplayed(false);
Chris@133 278 axes#setXTickRenderer(xtickLabels);
Chris@125 279 axes#setYAxeLabelDisplayed(false);
Chris@125 280 axes#setZAxeLabelDisplayed(true);
Chris@133 281 if opts.normalised then
Chris@133 282 axes#setZAxeLabel("");
Chris@133 283 axes#setZTickRenderer(newPercentTickRenderer ());
Chris@133 284 else
Chris@133 285 axes#setZAxeLabel(opts.unit);
Chris@133 286 fi;
Chris@125 287 axes#setYTickLabelDisplayed(false);
Chris@125 288 ChartLauncher#openChart(chart);
Chris@125 289 ());
Chris@124 290
Chris@110 291 plotStructure structure =
Chris@110 292 case structure of
Chris@110 293 Grid matrix:
Chris@115 294 plotMatrix matrix;
Chris@110 295 //!!!
Chris@110 296 _: failWith "Cannot plot this structure (only grids implemented so far)";
Chris@110 297 esac;
Chris@108 298
Chris@115 299 {
Chris@119 300 newMatrixMapper,
Chris@119 301 newMatrixLogMapper,
Chris@119 302 newMapper,
Chris@115 303 plotMatrix,
Chris@124 304 plotBarChart,
Chris@124 305 plotLines,
Chris@125 306 stack,
Chris@125 307 plotStacked,
Chris@119 308 plotStructure,
Chris@115 309 }
Chris@110 310