fd5387ae282b582fe86c40dbd50a104326119c8f
[dygraphs.git] / plotkit_v091 / PlotKit / Layout.js
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
16 try {
17 if (typeof(PlotKit.Base) == 'undefined')
18 {
19 throw ""
20 }
21 }
22 catch (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
30 if (typeof(PlotKit.Layout) == 'undefined') {
31 PlotKit.Layout = {};
32 }
33
34 PlotKit.Layout.NAME = "PlotKit.Layout";
35 PlotKit.Layout.VERSION = PlotKit.VERSION;
36
37 PlotKit.Layout.__repr__ = function() {
38 return "[" + this.NAME + " " + this.VERSION + "]";
39 };
40
41 PlotKit.Layout.toString = function() {
42 return this.__repr__();
43 }
44
45 PlotKit.Layout.valid_styles = ["bar", "line", "pie", "point"];
46
47 // --------------------------------------------------------------------
48 // Start of Layout definition
49 // --------------------------------------------------------------------
50
51 PlotKit.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
119 PlotKit.Layout.prototype.addDataset = function(setname, set_xy) {
120 this.datasets[setname] = set_xy;
121 };
122
123 PlotKit.Layout.prototype.removeDataset = function(setname, set_xy) {
124 delete this.datasets[setname];
125 };
126
127 // --------------------------------------------------------------------
128 // Evaluates the layout for the current data and style.
129 // --------------------------------------------------------------------
130
131 PlotKit.Layout.prototype.evaluate = function() {
132 this._evaluateLimits();
133 this._evaluateScales();
134 if (this.style == "line") {
135 this._evaluateLineCharts();
136 this._evaluateLineTicks();
137 }
138 };
139
140
141
142
143 // --------------------------------------------------------------------
144 // START Internal Functions
145 // --------------------------------------------------------------------
146
147 PlotKit.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
189 PlotKit.Layout.prototype._evaluateScales = function() {
190 this.xrange = this.maxxval - this.minxval;
191 if (this.xrange == 0)
192 this.xscale = 1.0;
193 else
194 this.xscale = 1/this.xrange;
195
196 this.yrange = this.maxyval - this.minyval;
197 if (this.yrange == 0)
198 this.yscale = 1.0;
199 else
200 this.yscale = 1/this.yrange;
201 };
202
203
204 // Create the line charts
205 PlotKit.Layout.prototype._evaluateLineCharts = function() {
206 var items = PlotKit.Base.items;
207
208 var setCount = items(this.datasets).length;
209
210 // add all the rects
211 this.points = new Array();
212 var i = 0;
213 for (var setName in this.datasets) {
214 var dataset = this.datasets[setName];
215 if (PlotKit.Base.isFuncLike(dataset)) continue;
216 dataset.sort(function(a, b) { return compare(parseFloat(a[0]), parseFloat(b[0])); });
217 for (var j = 0; j < dataset.length; j++) {
218 var item = dataset[j];
219 var point = {
220 x: ((parseFloat(item[0]) - this.minxval) * this.xscale),
221 y: 1.0 - ((parseFloat(item[1]) - this.minyval) * this.yscale),
222 xval: parseFloat(item[0]),
223 yval: parseFloat(item[1]),
224 name: setName
225 };
226
227 // limit the x, y values so they do not overdraw
228 if (point.y <= 0.0) {
229 point.y = 0.0;
230 }
231 if (point.y >= 1.0) {
232 point.y = 1.0;
233 }
234 if ((point.x >= 0.0) && (point.x <= 1.0)) {
235 this.points.push(point);
236 }
237 }
238 i++;
239 }
240 };
241
242
243 PlotKit.Layout.prototype._evaluateLineTicksForXAxis = function() {
244 var isNil = MochiKit.Base.isUndefinedOrNull;
245
246 this.xticks = new Array();
247 var makeTicks = function(tick) {
248 var label = tick.label;
249 if (isNil(label))
250 label = tick.v.toString();
251 var pos = this.xscale * (tick.v - this.minxval);
252 if ((pos >= 0.0) && (pos <= 1.0)) {
253 this.xticks.push([pos, label]);
254 }
255 };
256 MochiKit.Iter.forEach(this.options.xTicks, bind(makeTicks, this));
257 };
258
259 PlotKit.Layout.prototype._evaluateLineTicksForYAxis = function() {
260 var isNil = MochiKit.Base.isUndefinedOrNull;
261
262 this.yticks = new Array();
263 var makeTicks = function(tick) {
264 var label = tick.label;
265 if (isNil(label))
266 label = tick.v.toString();
267 var pos = 1.0 - (this.yscale * (tick.v - this.minyval));
268 if ((pos >= 0.0) && (pos <= 1.0)) {
269 this.yticks.push([pos, label]);
270 }
271 };
272 MochiKit.Iter.forEach(this.options.yTicks, bind(makeTicks, this));
273 };
274
275 PlotKit.Layout.prototype._evaluateLineTicks = function() {
276 this._evaluateLineTicksForXAxis();
277 this._evaluateLineTicksForYAxis();
278 };
279
280
281 // --------------------------------------------------------------------
282 // END Internal Functions
283 // --------------------------------------------------------------------
284
285
286 // Namespace Iniitialisation
287
288 PlotKit.LayoutModule = {};
289 PlotKit.LayoutModule.Layout = PlotKit.Layout;
290
291 PlotKit.LayoutModule.EXPORT = [
292 "Layout"
293 ];
294
295 PlotKit.LayoutModule.EXPORT_OK = [];
296
297 PlotKit.LayoutModule.__new__ = function() {
298 var m = MochiKit.Base;
299
300 m.nameFunctions(this);
301
302 this.EXPORT_TAGS = {
303 ":common": this.EXPORT,
304 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
305 };
306 };
307
308 PlotKit.LayoutModule.__new__();
309 MochiKit.Base._exportSymbols(this, PlotKit.LayoutModule);
310
311