keep hacking out of plotkit
[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 // --------------------------------------------------------------------
46 // Start of Layout definition
47 // --------------------------------------------------------------------
48
49 PlotKit.Layout = function(style, options) {
50
51 this.options = {
52 "xOriginIsZero": true,
53 "yOriginIsZero": true,
54 "xAxis": null, // [xmin, xmax]
55 "yAxis": null, // [ymin, ymax]
56 "xTicks": null, // [{label: "somelabel", v: value}, ..] (label opt.)
57 "yTicks": null, // [{label: "somelabel", v: value}, ..] (label opt.)
58 };
59
60 // valid external options : TODO: input verification
61 this.style = style;
62 MochiKit.Base.update(this.options, options ? options : {});
63
64 this.minxval = 0;
65 this.maxxval = null;
66 this.xscale = null; // val -> pos factor (eg, xval * xscale = xpos)
67
68 this.minyval = 0;
69 this.maxyval = null;
70 this.yscale = null;
71
72 this.points = new Array(); // array of points to plot for line plots
73
74 this.xticks = new Array();
75 this.yticks = new Array();
76
77 // internal states
78 this.datasets = new Array();
79 this.minxdelta = 0;
80 this.xrange = 1;
81 this.yrange = 1;
82
83 this.hitTestCache = {x2maxy: null};
84
85 };
86
87 // --------------------------------------------------------------------
88 // Dataset Manipulation
89 // --------------------------------------------------------------------
90
91
92 PlotKit.Layout.prototype.addDataset = function(setname, set_xy) {
93 this.datasets[setname] = set_xy;
94 };
95
96 PlotKit.Layout.prototype.removeDataset = function(setname, set_xy) {
97 delete this.datasets[setname];
98 };
99
100 // --------------------------------------------------------------------
101 // Evaluates the layout for the current data and style.
102 // --------------------------------------------------------------------
103
104 PlotKit.Layout.prototype.evaluate = function() {
105 this._evaluateLimits();
106 this._evaluateScales();
107 if (this.style == "line") {
108 this._evaluateLineCharts();
109 this._evaluateLineTicks();
110 }
111 };
112
113
114
115
116 // --------------------------------------------------------------------
117 // START Internal Functions
118 // --------------------------------------------------------------------
119
120 PlotKit.Layout.prototype._evaluateLimits = function() {
121 // take all values from all datasets and find max and min
122 var map = PlotKit.Base.map;
123 var items = PlotKit.Base.items;
124 var itemgetter = MochiKit.Base.itemgetter;
125 var collapse = PlotKit.Base.collapse;
126 var listMin = MochiKit.Base.listMin;
127 var listMax = MochiKit.Base.listMax;
128 var isNil = MochiKit.Base.isUndefinedOrNull;
129
130
131 var all = collapse(map(itemgetter(1), items(this.datasets)));
132 if (isNil(this.options.xAxis)) {
133 if (this.options.xOriginIsZero)
134 this.minxval = 0;
135 else
136 this.minxval = listMin(map(parseFloat, map(itemgetter(0), all)));
137
138 this.maxxval = listMax(map(parseFloat, map(itemgetter(0), all)));
139 }
140 else {
141 this.minxval = this.options.xAxis[0];
142 this.maxxval = this.options.xAxis[1];
143 this.xscale = this.maxval - this.minxval;
144 }
145
146 if (isNil(this.options.yAxis)) {
147 if (this.options.yOriginIsZero)
148 this.minyval = 0;
149 else
150 this.minyval = listMin(map(parseFloat, map(itemgetter(1), all)));
151
152 this.maxyval = listMax(map(parseFloat, map(itemgetter(1), all)));
153 }
154 else {
155 this.minyval = this.options.yAxis[0];
156 this.maxyval = this.options.yAxis[1];
157 this.yscale = this.maxyval - this.minyval;
158 }
159
160 };
161
162 PlotKit.Layout.prototype._evaluateScales = function() {
163 this.xrange = this.maxxval - this.minxval;
164 if (this.xrange == 0)
165 this.xscale = 1.0;
166 else
167 this.xscale = 1/this.xrange;
168
169 this.yrange = this.maxyval - this.minyval;
170 if (this.yrange == 0)
171 this.yscale = 1.0;
172 else
173 this.yscale = 1/this.yrange;
174 };
175
176
177 // Create the line charts
178 PlotKit.Layout.prototype._evaluateLineCharts = function() {
179 var items = PlotKit.Base.items;
180
181 var setCount = items(this.datasets).length;
182
183 // add all the rects
184 this.points = new Array();
185 var i = 0;
186 for (var setName in this.datasets) {
187 var dataset = this.datasets[setName];
188 if (PlotKit.Base.isFuncLike(dataset)) continue;
189 dataset.sort(function(a, b) { return compare(parseFloat(a[0]), parseFloat(b[0])); });
190 for (var j = 0; j < dataset.length; j++) {
191 var item = dataset[j];
192 var point = {
193 x: ((parseFloat(item[0]) - this.minxval) * this.xscale),
194 y: 1.0 - ((parseFloat(item[1]) - this.minyval) * this.yscale),
195 xval: parseFloat(item[0]),
196 yval: parseFloat(item[1]),
197 name: setName
198 };
199
200 // limit the x, y values so they do not overdraw
201 if (point.y <= 0.0) {
202 point.y = 0.0;
203 }
204 if (point.y >= 1.0) {
205 point.y = 1.0;
206 }
207 if ((point.x >= 0.0) && (point.x <= 1.0)) {
208 this.points.push(point);
209 }
210 }
211 i++;
212 }
213 };
214
215
216 PlotKit.Layout.prototype._evaluateLineTicksForXAxis = function() {
217 var isNil = MochiKit.Base.isUndefinedOrNull;
218
219 this.xticks = new Array();
220 var makeTicks = function(tick) {
221 var label = tick.label;
222 if (isNil(label))
223 label = tick.v.toString();
224 var pos = this.xscale * (tick.v - this.minxval);
225 if ((pos >= 0.0) && (pos <= 1.0)) {
226 this.xticks.push([pos, label]);
227 }
228 };
229 MochiKit.Iter.forEach(this.options.xTicks, bind(makeTicks, this));
230 };
231
232 PlotKit.Layout.prototype._evaluateLineTicksForYAxis = function() {
233 var isNil = MochiKit.Base.isUndefinedOrNull;
234
235 this.yticks = new Array();
236 var makeTicks = function(tick) {
237 var label = tick.label;
238 if (isNil(label))
239 label = tick.v.toString();
240 var pos = 1.0 - (this.yscale * (tick.v - this.minyval));
241 if ((pos >= 0.0) && (pos <= 1.0)) {
242 this.yticks.push([pos, label]);
243 }
244 };
245 MochiKit.Iter.forEach(this.options.yTicks, bind(makeTicks, this));
246 };
247
248 PlotKit.Layout.prototype._evaluateLineTicks = function() {
249 this._evaluateLineTicksForXAxis();
250 this._evaluateLineTicksForYAxis();
251 };
252
253
254 // --------------------------------------------------------------------
255 // END Internal Functions
256 // --------------------------------------------------------------------
257
258
259 // Namespace Iniitialisation
260
261 PlotKit.LayoutModule = {};
262 PlotKit.LayoutModule.Layout = PlotKit.Layout;
263
264 PlotKit.LayoutModule.EXPORT = [
265 "Layout"
266 ];
267
268 PlotKit.LayoutModule.EXPORT_OK = [];
269
270 PlotKit.LayoutModule.__new__ = function() {
271 var m = MochiKit.Base;
272
273 m.nameFunctions(this);
274
275 this.EXPORT_TAGS = {
276 ":common": this.EXPORT,
277 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
278 };
279 };
280
281 PlotKit.LayoutModule.__new__();
282 MochiKit.Base._exportSymbols(this, PlotKit.LayoutModule);
283
284