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
|