annotate yetilab/plot/plot.yeti @ 141:9852a273e559

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