28c5830d0a994c214adaf743fd2bea14b56c3060
[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.points = new Array(); // array of points to plot for line plots
98
99 this.xticks = new Array();
100 this.yticks = new Array();
101
102 // internal states
103 this.datasets = new Array();
104 this.minxdelta = 0;
105 this.xrange = 1;
106 this.yrange = 1;
107
108 this.hitTestCache = {x2maxy: null};
109
110 };
111
112 // --------------------------------------------------------------------
113 // Dataset Manipulation
114 // --------------------------------------------------------------------
115
116
117 PlotKit.Layout.prototype.addDataset = function(setname, set_xy) {
118 this.datasets[setname] = set_xy;
119 };
120
121 PlotKit.Layout.prototype.removeDataset = function(setname, set_xy) {
122 delete this.datasets[setname];
123 };
124
125 // --------------------------------------------------------------------
126 // Evaluates the layout for the current data and style.
127 // --------------------------------------------------------------------
128
129 PlotKit.Layout.prototype.evaluate = function() {
130 this._evaluateLimits();
131 this._evaluateScales();
132 if (this.style == "line") {
133 this._evaluateLineCharts();
134 this._evaluateLineTicks();
135 }
136 };
137
138
139
140
141 // --------------------------------------------------------------------
142 // START Internal Functions
143 // --------------------------------------------------------------------
144
145 PlotKit.Layout.prototype._evaluateLimits = function() {
146 // take all values from all datasets and find max and min
147 var map = PlotKit.Base.map;
148 var items = PlotKit.Base.items;
149 var itemgetter = MochiKit.Base.itemgetter;
150 var collapse = PlotKit.Base.collapse;
151 var listMin = MochiKit.Base.listMin;
152 var listMax = MochiKit.Base.listMax;
153 var isNil = MochiKit.Base.isUndefinedOrNull;
154
155
156 var all = collapse(map(itemgetter(1), items(this.datasets)));
157 if (isNil(this.options.xAxis)) {
158 if (this.options.xOriginIsZero)
159 this.minxval = 0;
160 else
161 this.minxval = listMin(map(parseFloat, map(itemgetter(0), all)));
162
163 this.maxxval = listMax(map(parseFloat, map(itemgetter(0), all)));
164 }
165 else {
166 this.minxval = this.options.xAxis[0];
167 this.maxxval = this.options.xAxis[1];
168 this.xscale = this.maxval - this.minxval;
169 }
170
171 if (isNil(this.options.yAxis)) {
172 if (this.options.yOriginIsZero)
173 this.minyval = 0;
174 else
175 this.minyval = listMin(map(parseFloat, map(itemgetter(1), all)));
176
177 this.maxyval = listMax(map(parseFloat, map(itemgetter(1), all)));
178 }
179 else {
180 this.minyval = this.options.yAxis[0];
181 this.maxyval = this.options.yAxis[1];
182 this.yscale = this.maxyval - this.minyval;
183 }
184
185 };
186
187 PlotKit.Layout.prototype._evaluateScales = function() {
188 this.xrange = this.maxxval - this.minxval;
189 if (this.xrange == 0)
190 this.xscale = 1.0;
191 else
192 this.xscale = 1/this.xrange;
193
194 this.yrange = this.maxyval - this.minyval;
195 if (this.yrange == 0)
196 this.yscale = 1.0;
197 else
198 this.yscale = 1/this.yrange;
199 };
200
201
202 // Create the line charts
203 PlotKit.Layout.prototype._evaluateLineCharts = function() {
204 var items = PlotKit.Base.items;
205
206 var setCount = items(this.datasets).length;
207
208 // add all the rects
209 this.points = new Array();
210 var i = 0;
211 for (var setName in this.datasets) {
212 var dataset = this.datasets[setName];
213 if (PlotKit.Base.isFuncLike(dataset)) continue;
214 dataset.sort(function(a, b) { return compare(parseFloat(a[0]), parseFloat(b[0])); });
215 for (var j = 0; j < dataset.length; j++) {
216 var item = dataset[j];
217 var point = {
218 x: ((parseFloat(item[0]) - this.minxval) * this.xscale),
219 y: 1.0 - ((parseFloat(item[1]) - this.minyval) * this.yscale),
220 xval: parseFloat(item[0]),
221 yval: parseFloat(item[1]),
222 name: setName
223 };
224
225 // limit the x, y values so they do not overdraw
226 if (point.y <= 0.0) {
227 point.y = 0.0;
228 }
229 if (point.y >= 1.0) {
230 point.y = 1.0;
231 }
232 if ((point.x >= 0.0) && (point.x <= 1.0)) {
233 this.points.push(point);
234 }
235 }
236 i++;
237 }
238 };
239
240
241 PlotKit.Layout.prototype._evaluateLineTicksForXAxis = function() {
242 var isNil = MochiKit.Base.isUndefinedOrNull;
243
244 this.xticks = new Array();
245 var makeTicks = function(tick) {
246 var label = tick.label;
247 if (isNil(label))
248 label = tick.v.toString();
249 var pos = this.xscale * (tick.v - this.minxval);
250 if ((pos >= 0.0) && (pos <= 1.0)) {
251 this.xticks.push([pos, label]);
252 }
253 };
254 MochiKit.Iter.forEach(this.options.xTicks, bind(makeTicks, this));
255 };
256
257 PlotKit.Layout.prototype._evaluateLineTicksForYAxis = function() {
258 var isNil = MochiKit.Base.isUndefinedOrNull;
259
260 this.yticks = new Array();
261 var makeTicks = function(tick) {
262 var label = tick.label;
263 if (isNil(label))
264 label = tick.v.toString();
265 var pos = 1.0 - (this.yscale * (tick.v - this.minyval));
266 if ((pos >= 0.0) && (pos <= 1.0)) {
267 this.yticks.push([pos, label]);
268 }
269 };
270 MochiKit.Iter.forEach(this.options.yTicks, bind(makeTicks, this));
271 };
272
273 PlotKit.Layout.prototype._evaluateLineTicks = function() {
274 this._evaluateLineTicksForXAxis();
275 this._evaluateLineTicksForYAxis();
276 };
277
278
279 // --------------------------------------------------------------------
280 // END Internal Functions
281 // --------------------------------------------------------------------
282
283
284 // Namespace Iniitialisation
285
286 PlotKit.LayoutModule = {};
287 PlotKit.LayoutModule.Layout = PlotKit.Layout;
288
289 PlotKit.LayoutModule.EXPORT = [
290 "Layout"
291 ];
292
293 PlotKit.LayoutModule.EXPORT_OK = [];
294
295 PlotKit.LayoutModule.__new__ = function() {
296 var m = MochiKit.Base;
297
298 m.nameFunctions(this);
299
300 this.EXPORT_TAGS = {
301 ":common": this.EXPORT,
302 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
303 };
304 };
305
306 PlotKit.LayoutModule.__new__();
307 MochiKit.Base._exportSymbols(this, PlotKit.LayoutModule);
308
309