Merge branch 'gadget'
[dygraphs.git] / plotkit_v091 / PlotKit / Layout.js
CommitLineData
6a1aa64f
DV
1/*
2 PlotKit Layout
3 ==============
4
5 Handles laying out data on to a virtual canvas square canvas between 0.0
6 and 1.0. If you want to add new chart/plot types such as point plots,
7 you need to add them here.
8
9 Copyright
10 ---------
11 Copyright 2005,2006 (c) Alastair Tse <alastair^liquidx.net>
12 For use under the BSD license. <http://www.liquidx.net/plotkit>
13
14*/
15
16try {
17 if (typeof(PlotKit.Base) == 'undefined')
18 {
19 throw ""
20 }
21}
22catch (e) {
23 throw "PlotKit.Layout depends on MochiKit.{Base,Color,DOM,Format} and PlotKit.Base"
24}
25
26// --------------------------------------------------------------------
27// Start of Layout definition
28// --------------------------------------------------------------------
29
30if (typeof(PlotKit.Layout) == 'undefined') {
31 PlotKit.Layout = {};
32}
33
34PlotKit.Layout.NAME = "PlotKit.Layout";
35PlotKit.Layout.VERSION = PlotKit.VERSION;
36
37PlotKit.Layout.__repr__ = function() {
38 return "[" + this.NAME + " " + this.VERSION + "]";
39};
40
41PlotKit.Layout.toString = function() {
42 return this.__repr__();
43}
44
45PlotKit.Layout.valid_styles = ["bar", "line", "pie", "point"];
46
47// --------------------------------------------------------------------
48// Start of Layout definition
49// --------------------------------------------------------------------
50
51PlotKit.Layout = function(style, options) {
52
53 this.options = {
54 "barWidthFillFraction": 0.75,
55 "barOrientation": "vertical",
56 "xOriginIsZero": true,
57 "yOriginIsZero": true,
58 "xAxis": null, // [xmin, xmax]
59 "yAxis": null, // [ymin, ymax]
60 "xTicks": null, // [{label: "somelabel", v: value}, ..] (label opt.)
61 "yTicks": null, // [{label: "somelabel", v: value}, ..] (label opt.)
62 "xNumberOfTicks": 10,
63 "yNumberOfTicks": 5,
64 "xTickPrecision": 1,
65 "yTickPrecision": 1,
66 "pieRadius": 0.4
67 };
68
69 // valid external options : TODO: input verification
70 this.style = style;
71 MochiKit.Base.update(this.options, options ? options : {});
72
73 // externally visible states
74 // overriden if xAxis and yAxis are set in options
75 if (!MochiKit.Base.isUndefinedOrNull(this.options.xAxis)) {
76 this.minxval = this.options.xAxis[0];
77 this.maxxval = this.options.xAxis[1];
78 this.xscale = this.maxxval - this.minxval;
79 }
80 else {
81 this.minxval = 0;
82 this.maxxval = null;
83 this.xscale = null; // val -> pos factor (eg, xval * xscale = xpos)
84 }
85
86 if (!MochiKit.Base.isUndefinedOrNull(this.options.yAxis)) {
87 this.minyval = this.options.yAxis[0];
88 this.maxyval = this.options.yAxis[1];
89 this.yscale = this.maxyval - this.minyval;
90 }
91 else {
92 this.minyval = 0;
93 this.maxyval = null;
94 this.yscale = null;
95 }
96
97 this.bars = new Array(); // array of bars to plot for bar charts
98 this.points = new Array(); // array of points to plot for line plots
99 this.slices = new Array(); // array of slices to draw for pie charts
100
101 this.xticks = new Array();
102 this.yticks = new Array();
103
104 // internal states
105 this.datasets = new Array();
106 this.minxdelta = 0;
107 this.xrange = 1;
108 this.yrange = 1;
109
110 this.hitTestCache = {x2maxy: null};
111
112};
113
114// --------------------------------------------------------------------
115// Dataset Manipulation
116// --------------------------------------------------------------------
117
118
119PlotKit.Layout.prototype.addDataset = function(setname, set_xy) {
120 this.datasets[setname] = set_xy;
121};
122
123PlotKit.Layout.prototype.removeDataset = function(setname, set_xy) {
124 delete this.datasets[setname];
125};
126
6a1aa64f
DV
127// --------------------------------------------------------------------
128// Evaluates the layout for the current data and style.
129// --------------------------------------------------------------------
130
131PlotKit.Layout.prototype.evaluate = function() {
132 this._evaluateLimits();
133 this._evaluateScales();
9148174b 134 if (this.style == "line") {
6a1aa64f
DV
135 this._evaluateLineCharts();
136 this._evaluateLineTicks();
137 }
6a1aa64f
DV
138};
139
140
141
6a1aa64f
DV
142
143// --------------------------------------------------------------------
144// START Internal Functions
145// --------------------------------------------------------------------
146
147PlotKit.Layout.prototype._evaluateLimits = function() {
148 // take all values from all datasets and find max and min
149 var map = PlotKit.Base.map;
150 var items = PlotKit.Base.items;
151 var itemgetter = MochiKit.Base.itemgetter;
152 var collapse = PlotKit.Base.collapse;
153 var listMin = MochiKit.Base.listMin;
154 var listMax = MochiKit.Base.listMax;
155 var isNil = MochiKit.Base.isUndefinedOrNull;
156
157
158 var all = collapse(map(itemgetter(1), items(this.datasets)));
159 if (isNil(this.options.xAxis)) {
160 if (this.options.xOriginIsZero)
161 this.minxval = 0;
162 else
163 this.minxval = listMin(map(parseFloat, map(itemgetter(0), all)));
164
165 this.maxxval = listMax(map(parseFloat, map(itemgetter(0), all)));
166 }
167 else {
168 this.minxval = this.options.xAxis[0];
169 this.maxxval = this.options.xAxis[1];
170 this.xscale = this.maxval - this.minxval;
171 }
172
173 if (isNil(this.options.yAxis)) {
174 if (this.options.yOriginIsZero)
175 this.minyval = 0;
176 else
177 this.minyval = listMin(map(parseFloat, map(itemgetter(1), all)));
178
179 this.maxyval = listMax(map(parseFloat, map(itemgetter(1), all)));
180 }
181 else {
182 this.minyval = this.options.yAxis[0];
183 this.maxyval = this.options.yAxis[1];
184 this.yscale = this.maxyval - this.minyval;
185 }
186
187};
188
189PlotKit.Layout.prototype._evaluateScales = function() {
190 var isNil = MochiKit.Base.isUndefinedOrNull;
191
192 this.xrange = this.maxxval - this.minxval;
193 if (this.xrange == 0)
194 this.xscale = 1.0;
195 else
196 this.xscale = 1/this.xrange;
197
198 this.yrange = this.maxyval - this.minyval;
199 if (this.yrange == 0)
200 this.yscale = 1.0;
201 else
202 this.yscale = 1/this.yrange;
203};
204
205PlotKit.Layout.prototype._uniqueXValues = function() {
206 var collapse = PlotKit.Base.collapse;
207 var map = PlotKit.Base.map;
208 var uniq = PlotKit.Base.uniq;
209 var getter = MochiKit.Base.itemgetter;
210 var items = PlotKit.Base.items;
211
212 var xvalues = map(parseFloat, map(getter(0), collapse(map(getter(1), items(this.datasets)))));
213 xvalues.sort(MochiKit.Base.compare);
214 return uniq(xvalues);
215};
216
6a1aa64f
DV
217
218// Create the line charts
219PlotKit.Layout.prototype._evaluateLineCharts = function() {
220 var items = PlotKit.Base.items;
221
222 var setCount = items(this.datasets).length;
223
224 // add all the rects
225 this.points = new Array();
226 var i = 0;
227 for (var setName in this.datasets) {
228 var dataset = this.datasets[setName];
229 if (PlotKit.Base.isFuncLike(dataset)) continue;
230 dataset.sort(function(a, b) { return compare(parseFloat(a[0]), parseFloat(b[0])); });
231 for (var j = 0; j < dataset.length; j++) {
232 var item = dataset[j];
233 var point = {
234 x: ((parseFloat(item[0]) - this.minxval) * this.xscale),
235 y: 1.0 - ((parseFloat(item[1]) - this.minyval) * this.yscale),
236 xval: parseFloat(item[0]),
237 yval: parseFloat(item[1]),
238 name: setName
239 };
240
241 // limit the x, y values so they do not overdraw
242 if (point.y <= 0.0) {
243 point.y = 0.0;
244 }
245 if (point.y >= 1.0) {
246 point.y = 1.0;
247 }
248 if ((point.x >= 0.0) && (point.x <= 1.0)) {
249 this.points.push(point);
250 }
251 }
252 i++;
253 }
254};
255
6a1aa64f
DV
256
257PlotKit.Layout.prototype._evaluateLineTicksForXAxis = function() {
258 var isNil = MochiKit.Base.isUndefinedOrNull;
259
260 if (this.options.xTicks) {
261 // we use use specified ticks with optional labels
262
263 this.xticks = new Array();
264 var makeTicks = function(tick) {
265 var label = tick.label;
266 if (isNil(label))
267 label = tick.v.toString();
268 var pos = this.xscale * (tick.v - this.minxval);
269 if ((pos >= 0.0) && (pos <= 1.0)) {
270 this.xticks.push([pos, label]);
271 }
272 };
273 MochiKit.Iter.forEach(this.options.xTicks, bind(makeTicks, this));
274 }
275 else if (this.options.xNumberOfTicks) {
276 // we use defined number of ticks as hint to auto generate
277 var xvalues = this._uniqueXValues();
278 var roughSeparation = this.xrange / this.options.xNumberOfTicks;
279 var tickCount = 0;
280
281 this.xticks = new Array();
282 for (var i = 0; i <= xvalues.length; i++) {
283 if ((xvalues[i] - this.minxval) >= (tickCount * roughSeparation)) {
284 var pos = this.xscale * (xvalues[i] - this.minxval);
285 if ((pos > 1.0) || (pos < 0.0))
286 continue;
287 this.xticks.push([pos, xvalues[i]]);
288 tickCount++;
289 }
290 if (tickCount > this.options.xNumberOfTicks)
291 break;
292 }
293 }
294};
295
296PlotKit.Layout.prototype._evaluateLineTicksForYAxis = function() {
297 var isNil = MochiKit.Base.isUndefinedOrNull;
298
299
300 if (this.options.yTicks) {
301 this.yticks = new Array();
302 var makeTicks = function(tick) {
303 var label = tick.label;
304 if (isNil(label))
305 label = tick.v.toString();
306 var pos = 1.0 - (this.yscale * (tick.v - this.minyval));
307 if ((pos >= 0.0) && (pos <= 1.0)) {
308 this.yticks.push([pos, label]);
309 }
310 };
311 MochiKit.Iter.forEach(this.options.yTicks, bind(makeTicks, this));
312 }
6a1aa64f
DV
313};
314
315PlotKit.Layout.prototype._evaluateLineTicks = function() {
316 this._evaluateLineTicksForXAxis();
317 this._evaluateLineTicksForYAxis();
318};
319
6a1aa64f
DV
320
321// --------------------------------------------------------------------
322// END Internal Functions
323// --------------------------------------------------------------------
324
325
326// Namespace Iniitialisation
327
328PlotKit.LayoutModule = {};
329PlotKit.LayoutModule.Layout = PlotKit.Layout;
330
331PlotKit.LayoutModule.EXPORT = [
332 "Layout"
333];
334
335PlotKit.LayoutModule.EXPORT_OK = [];
336
337PlotKit.LayoutModule.__new__ = function() {
338 var m = MochiKit.Base;
339
340 m.nameFunctions(this);
341
342 this.EXPORT_TAGS = {
343 ":common": this.EXPORT,
344 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
345 };
346};
347
348PlotKit.LayoutModule.__new__();
349MochiKit.Base._exportSymbols(this, PlotKit.LayoutModule);
350
351