Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(75)

Side by Side Diff: static/js/plotkit/Layout.js

Issue 22969002: Add stats js files to static dir (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/chromium-build
Patch Set: Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « static/js/plotkit/LICENSE ('k') | static/js/plotkit/README.chromium » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 =======
3 PlotKit is a collection of Javascript classes that allows
4 you to quickly visualise data using different types of charts.
5
6 http://www.liquidx.net/plotkit/
7
8 See LICENSE file for copyright information.
9
10 Handles laying out data on to a virtual canvas square canvas between 0.0
11 and 1.0. If you want to add new chart/plot types such as point plots,
12 you need to add them here.
13 */
14
15 try {
16 if (typeof(PlotKit.Base) == 'undefined')
17 {
18 throw ""
19 }
20 }
21 catch (e) {
22 throw "PlotKit.Layout depends on MochiKit.{Base,Color,DOM,Format} and PlotKi t.Base"
23 }
24
25 // --------------------------------------------------------------------
26 // Start of Layout definition
27 // --------------------------------------------------------------------
28
29 if (typeof(PlotKit.Layout) == 'undefined') {
30 PlotKit.Layout = {};
31 }
32
33 PlotKit.Layout.NAME = "PlotKit.Layout";
34 PlotKit.Layout.VERSION = PlotKit.VERSION;
35
36 PlotKit.Layout.__repr__ = function() {
37 return "[" + this.NAME + " " + this.VERSION + "]";
38 };
39
40 PlotKit.Layout.toString = function() {
41 return this.__repr__();
42 }
43
44 PlotKit.Layout.valid_styles = ["bar", "line", "pie", "point"];
45
46 // --------------------------------------------------------------------
47 // Start of Layout definition
48 // --------------------------------------------------------------------
49
50 PlotKit.Layout = function(style, options) {
51
52 this.options = {
53 "barWidthFillFraction": 0.75,
54 "barOrientation": "vertical",
55 "xOriginIsZero": true,
56 "yOriginIsZero": true,
57 "xAxis": null, // [xmin, xmax]
58 "yAxis": null, // [ymin, ymax]
59 "xTicks": null, // [{label: "somelabel", v: value}, ..] (label opt.)
60 "yTicks": null, // [{label: "somelabel", v: value}, ..] (label opt.)
61 "xNumberOfTicks": 10,
62 "yNumberOfTicks": 5,
63 "xTickPrecision": 1,
64 "yTickPrecision": 1,
65 "pieRadius": 0.4
66 };
67
68 // valid external options : TODO: input verification
69 this.style = style;
70 MochiKit.Base.update(this.options, options ? options : {});
71
72 // externally visible states
73 // overriden if xAxis and yAxis are set in options
74 if (!MochiKit.Base.isUndefinedOrNull(this.options.xAxis)) {
75 this.minxval = this.options.xAxis[0];
76 this.maxxval = this.options.xAxis[1];
77 this.xscale = this.maxxval - this.minxval;
78 }
79 else {
80 this.minxval = 0;
81 this.maxxval = null;
82 this.xscale = null; // val -> pos factor (eg, xval * xscale = xpos)
83 }
84
85 if (!MochiKit.Base.isUndefinedOrNull(this.options.yAxis)) {
86 this.minyval = this.options.yAxis[0];
87 this.maxyval = this.options.yAxis[1];
88 this.yscale = this.maxyval - this.minyval;
89 }
90 else {
91 this.minyval = 0;
92 this.maxyval = null;
93 this.yscale = null;
94 }
95
96 this.bars = new Array(); // array of bars to plot for bar charts
97 this.points = new Array(); // array of points to plot for line plots
98 this.slices = new Array(); // array of slices to draw for pie charts
99
100 this.xticks = new Array();
101 this.yticks = new Array();
102
103 // internal states
104 this.datasets = new Array();
105 this.minxdelta = 0;
106 this.xrange = 1;
107 this.yrange = 1;
108
109 this.hitTestCache = {x2maxy: null};
110
111 };
112
113 // --------------------------------------------------------------------
114 // Dataset Manipulation
115 // --------------------------------------------------------------------
116
117
118 PlotKit.Layout.prototype.addDataset = function(setname, set_xy) {
119 this.datasets[setname] = set_xy;
120 };
121
122 PlotKit.Layout.prototype.removeDataset = function(setname, set_xy) {
123 delete this.datasets[setname];
124 };
125
126 PlotKit.Layout.prototype.addDatasetFromTable = function(name, tableElement, xcol , ycol, lcol) {
127 var isNil = MochiKit.Base.isUndefinedOrNull;
128 var scrapeText = MochiKit.DOM.scrapeText;
129 var strip = MochiKit.Format.strip;
130
131 if (isNil(xcol))
132 xcol = 0;
133 if (isNil(ycol))
134 ycol = 1;
135 if (isNil(lcol))
136 lcol = -1;
137
138 var rows = tableElement.tBodies[0].rows;
139 var data = new Array();
140 var labels = new Array();
141
142 if (!isNil(rows)) {
143 for (var i = 0; i < rows.length; i++) {
144 data.push([parseFloat(strip(scrapeText(rows[i].cells[xcol]))),
145 parseFloat(strip(scrapeText(rows[i].cells[ycol])))]);
146 if (lcol >= 0){
147 labels.push({v: parseFloat(strip(scrapeText(rows[i].cells[xcol])) ),
148 label: strip(scrapeText(rows[i].cells[lcol]))});
149 }
150 }
151 this.addDataset(name, data);
152 if (lcol >= 0) {
153 this.options.xTicks = labels;
154 }
155 return true;
156 }
157 return false;
158 };
159
160 // --------------------------------------------------------------------
161 // Evaluates the layout for the current data and style.
162 // --------------------------------------------------------------------
163
164 PlotKit.Layout.prototype.evaluate = function() {
165 this._evaluateLimits();
166 this._evaluateScales();
167 if (this.style == "bar") {
168 if (this.options.barOrientation == "horizontal") {
169 this._evaluateHorizBarCharts();
170 }
171 else {
172 this._evaluateBarCharts();
173 }
174 this._evaluateBarTicks();
175 }
176 else if (this.style == "line") {
177 this._evaluateLineCharts();
178 this._evaluateLineTicks();
179 }
180 else if (this.style == "pie") {
181 this._evaluatePieCharts();
182 this._evaluatePieTicks();
183 }
184 };
185
186
187
188 // Given the fractional x, y positions, report the corresponding
189 // x, y values.
190 PlotKit.Layout.prototype.hitTest = function(x, y) {
191 // TODO: make this more efficient with better datastructures
192 // for this.bars, this.points and this.slices
193
194 var f = MochiKit.Format.twoDigitFloat;
195
196 if ((this.style == "bar") && this.bars && (this.bars.length > 0)) {
197 for (var i = 0; i < this.bars.length; i++) {
198 var bar = this.bars[i];
199 if ((x >= bar.x) && (x <= bar.x + bar.w)
200 && (y >= bar.y) && (y - bar.y <= bar.h))
201 return bar;
202 }
203 }
204
205 else if (this.style == "line") {
206 if (this.hitTestCache.x2maxy == null) {
207 this._regenerateHitTestCache();
208 }
209
210 // 1. find the xvalues that equal or closest to the give x
211 var xval = x / this.xscale;
212 var xvalues = this.hitTestCache.xvalues;
213 var xbefore = null;
214 var xafter = null;
215
216 for (var i = 1; i < xvalues.length; i++) {
217 if (xvalues[i] > xval) {
218 xbefore = xvalues[i-1];
219 xafter = xvalues[i];
220 break;
221 }
222 }
223
224 if ((xbefore != null)) {
225 var ybefore = this.hitTestCache.x2maxy[xbefore];
226 var yafter = this.hitTestCache.x2maxy[xafter];
227 var yval = (1.0 - y)/this.yscale;
228
229 // interpolate whether we will fall inside or outside
230 var gradient = (yafter - ybefore) / (xafter - xbefore);
231 var projmaxy = ybefore + gradient * (xval - xbefore);
232 if (projmaxy >= yval) {
233 // inside the highest curve (roughly)
234 var obj = {xval: xval, yval: yval,
235 xafter: xafter, yafter: yafter,
236 xbefore: xbefore, ybefore: ybefore,
237 yprojected: projmaxy
238 };
239 return obj;
240 }
241 }
242 }
243
244 else if (this.style == "pie") {
245 var dist = Math.sqrt((y-0.5)*(y-0.5) + (x-0.5)*(x-0.5));
246 if (dist > this.options.pieRadius)
247 return null;
248
249 // TODO: actually doesn't work if we don't know how the Canvas
250 // lays it out, need to fix!
251 var angle = Math.atan2(y - 0.5, x - 0.5) - Math.PI/2;
252 for (var i = 0; i < this.slices.length; i++) {
253 var slice = this.slices[i];
254 if (slice.startAngle < angle && slice.endAngle >= angle)
255 return slice;
256 }
257 }
258
259 return null;
260 };
261
262 // Reports valid position rectangle for X value (only valid for bar charts)
263 PlotKit.Layout.prototype.rectForX = function(x) {
264 return null;
265 };
266
267 // Reports valid angles through which X value encloses (only valid for pie chart s)
268 PlotKit.Layout.prototype.angleRangeForX = function(x) {
269 return null;
270 };
271
272 // --------------------------------------------------------------------
273 // START Internal Functions
274 // --------------------------------------------------------------------
275
276 PlotKit.Layout.prototype._evaluateLimits = function() {
277 // take all values from all datasets and find max and min
278 var map = PlotKit.Base.map;
279 var items = PlotKit.Base.items;
280 var itemgetter = MochiKit.Base.itemgetter;
281 var collapse = PlotKit.Base.collapse;
282 var listMin = MochiKit.Base.listMin;
283 var listMax = MochiKit.Base.listMax;
284 var isNil = MochiKit.Base.isUndefinedOrNull;
285
286
287 var all = collapse(map(itemgetter(1), items(this.datasets)));
288 if (isNil(this.options.xAxis)) {
289 if (this.options.xOriginIsZero)
290 this.minxval = 0;
291 else
292 this.minxval = listMin(map(parseFloat, map(itemgetter(0), all)));
293
294 this.maxxval = listMax(map(parseFloat, map(itemgetter(0), all)));
295 }
296 else {
297 this.minxval = this.options.xAxis[0];
298 this.maxxval = this.options.xAxis[1];
299 this.xscale = this.maxval - this.minxval;
300 }
301
302 if (isNil(this.options.yAxis)) {
303 if (this.options.yOriginIsZero)
304 this.minyval = 0;
305 else
306 this.minyval = listMin(map(parseFloat, map(itemgetter(1), all)));
307
308 this.maxyval = listMax(map(parseFloat, map(itemgetter(1), all)));
309 }
310 else {
311 this.minyval = this.options.yAxis[0];
312 this.maxyval = this.options.yAxis[1];
313 this.yscale = this.maxyval - this.minyval;
314 }
315
316 };
317
318 PlotKit.Layout.prototype._evaluateScales = function() {
319 var isNil = MochiKit.Base.isUndefinedOrNull;
320
321 this.xrange = this.maxxval - this.minxval;
322 if (this.xrange == 0)
323 this.xscale = 1.0;
324 else
325 this.xscale = 1/this.xrange;
326
327 this.yrange = this.maxyval - this.minyval;
328 if (this.yrange == 0)
329 this.yscale = 1.0;
330 else
331 this.yscale = 1/this.yrange;
332 };
333
334 PlotKit.Layout.prototype._uniqueXValues = function() {
335 var collapse = PlotKit.Base.collapse;
336 var map = PlotKit.Base.map;
337 var uniq = PlotKit.Base.uniq;
338 var getter = MochiKit.Base.itemgetter;
339 var items = PlotKit.Base.items;
340
341 var xvalues = map(parseFloat, map(getter(0), collapse(map(getter(1), items(t his.datasets)))));
342 xvalues.sort(MochiKit.Base.compare);
343 return uniq(xvalues);
344 };
345
346 // Create the bars
347 PlotKit.Layout.prototype._evaluateBarCharts = function() {
348 var items = PlotKit.Base.items;
349
350 var setCount = items(this.datasets).length;
351
352 // work out how far separated values are
353 var xdelta = 10000000;
354 var xvalues = this._uniqueXValues();
355 for (var i = 1; i < xvalues.length; i++) {
356 xdelta = Math.min(Math.abs(xvalues[i] - xvalues[i-1]), xdelta);
357 }
358
359 var barWidth = 0;
360 var barWidthForSet = 0;
361 var barMargin = 0;
362 if (xvalues.length == 1) {
363 // note we have to do something smarter if we only plot one value
364 xdelta = 1.0;
365 this.xscale = 1.0;
366 this.minxval = xvalues[0];
367 barWidth = 1.0 * this.options.barWidthFillFraction;
368 barWidthForSet = barWidth/setCount;
369 barMargin = (1.0 - this.options.barWidthFillFraction)/2;
370 }
371 else {
372 // readjust xscale to fix with bar charts
373 if (this.xrange == 1) {
374 this.xscale = 0.5;
375 }
376 else if (this.xrange == 2) {
377 this.xscale = 1/3.0;
378 }
379 else {
380 this.xscale = (1.0 - xdelta/this.xrange)/this.xrange;
381 }
382 barWidth = xdelta * this.xscale * this.options.barWidthFillFraction;
383 barWidthForSet = barWidth / setCount;
384 barMargin = xdelta * this.xscale * (1.0 - this.options.barWidthFillFract ion)/2;
385 }
386
387 this.minxdelta = xdelta; // need this for tick positions
388
389 // add all the rects
390 this.bars = new Array();
391 var i = 0;
392 for (var setName in this.datasets) {
393 var dataset = this.datasets[setName];
394 if (PlotKit.Base.isFuncLike(dataset)) continue;
395 for (var j = 0; j < dataset.length; j++) {
396 var item = dataset[j];
397 var rect = {
398 x: ((parseFloat(item[0]) - this.minxval) * this.xscale) + (i * b arWidthForSet) + barMargin,
399 y: 1.0 - ((parseFloat(item[1]) - this.minyval) * this.yscale),
400 w: barWidthForSet,
401 h: ((parseFloat(item[1]) - this.minyval) * this.yscale),
402 xval: parseFloat(item[0]),
403 yval: parseFloat(item[1]),
404 name: setName
405 };
406 if ((rect.x >= 0.0) && (rect.x <= 1.0) &&
407 (rect.y >= 0.0) && (rect.y <= 1.0)) {
408 this.bars.push(rect);
409 }
410 }
411 i++;
412 }
413 };
414
415 // Create the horizontal bars
416 PlotKit.Layout.prototype._evaluateHorizBarCharts = function() {
417 var items = PlotKit.Base.items;
418
419 var setCount = items(this.datasets).length;
420
421 // work out how far separated values are
422 var xdelta = 10000000;
423 var xvalues = this._uniqueXValues();
424 for (var i = 1; i < xvalues.length; i++) {
425 xdelta = Math.min(Math.abs(xvalues[i] - xvalues[i-1]), xdelta);
426 }
427
428 var barWidth = 0;
429 var barWidthForSet = 0;
430 var barMargin = 0;
431
432 // work out how far each far each bar is separated
433 if (xvalues.length == 1) {
434 // do something smarter if we only plot one value
435 xdelta = 1.0;
436 this.xscale = 1.0;
437 this.minxval = xvalues[0];
438 barWidth = 1.0 * this.options.barWidthFillFraction;
439 barWidthForSet = barWidth/setCount;
440 barMargin = (1.0 - this.options.barWidthFillFraction)/2;
441 }
442 else {
443 // readjust yscale to fix with bar charts
444 this.xscale = (1.0 - xdelta/this.xrange)/this.xrange;
445 barWidth = xdelta * this.xscale * this.options.barWidthFillFraction;
446 barWidthForSet = barWidth / setCount;
447 barMargin = xdelta * this.xscale * (1.0 - this.options.barWidthFillFract ion)/2;
448 }
449
450 this.minxdelta = xdelta; // need this for tick positions
451
452 // add all the rects
453 this.bars = new Array();
454 var i = 0;
455 for (var setName in this.datasets) {
456 var dataset = this.datasets[setName];
457 if (PlotKit.Base.isFuncLike(dataset)) continue;
458 for (var j = 0; j < dataset.length; j++) {
459 var item = dataset[j];
460 var rect = {
461 y: ((parseFloat(item[0]) - this.minxval) * this.xscale) + (i * b arWidthForSet) + barMargin,
462 x: 0.0,
463 h: barWidthForSet,
464 w: ((parseFloat(item[1]) - this.minyval) * this.yscale),
465 xval: parseFloat(item[0]),
466 yval: parseFloat(item[1]),
467 name: setName
468 };
469
470 // limit the x, y values so they do not overdraw
471 if (rect.y <= 0.0) {
472 rect.y = 0.0;
473 }
474 if (rect.y >= 1.0) {
475 rect.y = 1.0;
476 }
477 if ((rect.x >= 0.0) && (rect.x <= 1.0)) {
478 this.bars.push(rect);
479 }
480 }
481 i++;
482 }
483 };
484
485
486 // Create the line charts
487 PlotKit.Layout.prototype._evaluateLineCharts = function() {
488 var items = PlotKit.Base.items;
489
490 var setCount = items(this.datasets).length;
491
492 // add all the rects
493 this.points = new Array();
494 var i = 0;
495 for (var setName in this.datasets) {
496 var dataset = this.datasets[setName];
497 if (PlotKit.Base.isFuncLike(dataset)) continue;
498 dataset.sort(function(a, b) { return compare(parseFloat(a[0]), parseFloa t(b[0])); });
499 for (var j = 0; j < dataset.length; j++) {
500 var item = dataset[j];
501 var point = {
502 x: ((parseFloat(item[0]) - this.minxval) * this.xscale),
503 y: 1.0 - ((parseFloat(item[1]) - this.minyval) * this.yscale),
504 xval: parseFloat(item[0]),
505 yval: parseFloat(item[1]),
506 name: setName
507 };
508
509 // limit the x, y values so they do not overdraw
510 if (point.y <= 0.0) {
511 point.y = 0.0;
512 }
513 if (point.y >= 1.0) {
514 point.y = 1.0;
515 }
516 if ((point.x >= 0.0) && (point.x <= 1.0)) {
517 this.points.push(point);
518 }
519 }
520 i++;
521 }
522 };
523
524 // Create the pie charts
525 PlotKit.Layout.prototype._evaluatePieCharts = function() {
526 var items = PlotKit.Base.items;
527 var sum = MochiKit.Iter.sum;
528 var getter = MochiKit.Base.itemgetter;
529
530 var setCount = items(this.datasets).length;
531
532 // we plot the y values of the first dataset
533 var dataset = items(this.datasets)[0][1];
534 var total = sum(map(getter(1), dataset));
535
536 this.slices = new Array();
537 var currentAngle = 0.0;
538 for (var i = 0; i < dataset.length; i++) {
539 var fraction = dataset[i][1] / total;
540 var startAngle = currentAngle * Math.PI * 2;
541 var endAngle = (currentAngle + fraction) * Math.PI * 2;
542
543 var slice = {fraction: fraction,
544 xval: dataset[i][0],
545 yval: dataset[i][1],
546 startAngle: startAngle,
547 endAngle: endAngle
548 };
549 if (dataset[i][1] != 0) {
550 this.slices.push(slice);
551 }
552 currentAngle += fraction;
553 }
554 };
555
556 PlotKit.Layout.prototype._evaluateLineTicksForXAxis = function() {
557 var isNil = MochiKit.Base.isUndefinedOrNull;
558
559 if (this.options.xTicks) {
560 // we use use specified ticks with optional labels
561
562 this.xticks = new Array();
563 var makeTicks = function(tick) {
564 var label = tick.label;
565 if (isNil(label))
566 label = tick.v.toString();
567 var pos = this.xscale * (tick.v - this.minxval);
568 if ((pos >= 0.0) && (pos <= 1.0)) {
569 this.xticks.push([pos, label]);
570 }
571 };
572 MochiKit.Iter.forEach(this.options.xTicks, bind(makeTicks, this));
573 }
574 else if (this.options.xNumberOfTicks) {
575 // we use defined number of ticks as hint to auto generate
576 var xvalues = this._uniqueXValues();
577 var roughSeparation = this.xrange / this.options.xNumberOfTicks;
578 var tickCount = 0;
579
580 this.xticks = new Array();
581 for (var i = 0; i <= xvalues.length; i++) {
582 if ((xvalues[i] - this.minxval) >= (tickCount * roughSeparation)) {
583 var pos = this.xscale * (xvalues[i] - this.minxval);
584 if ((pos > 1.0) || (pos < 0.0))
585 continue;
586 this.xticks.push([pos, xvalues[i]]);
587 tickCount++;
588 }
589 if (tickCount > this.options.xNumberOfTicks)
590 break;
591 }
592 }
593 };
594
595 PlotKit.Layout.prototype._evaluateLineTicksForYAxis = function() {
596 var isNil = MochiKit.Base.isUndefinedOrNull;
597
598
599 if (this.options.yTicks) {
600 this.yticks = new Array();
601 var makeTicks = function(tick) {
602 var label = tick.label;
603 if (isNil(label))
604 label = tick.v.toString();
605 var pos = 1.0 - (this.yscale * (tick.v - this.minyval));
606 if ((pos >= 0.0) && (pos <= 1.0)) {
607 this.yticks.push([pos, label]);
608 }
609 };
610 MochiKit.Iter.forEach(this.options.yTicks, bind(makeTicks, this));
611 }
612 else if (this.options.yNumberOfTicks) {
613 // We use the optionally defined number of ticks as a guide
614 this.yticks = new Array();
615
616 // if we get this separation right, we'll have good looking graphs
617 var roundInt = PlotKit.Base.roundInterval;
618 var prec = this.options.yTickPrecision;
619 var roughSeparation = roundInt(this.yrange,
620 this.options.yNumberOfTicks, prec);
621
622 // round off each value of the y-axis to the precision
623 // eg. 1.3333 at precision 1 -> 1.3
624 for (var i = 0; i <= this.options.yNumberOfTicks; i++) {
625 var yval = this.minyval + (i * roughSeparation);
626 var pos = 1.0 - ((yval - this.minyval) * this.yscale);
627 if ((pos > 1.0) || (pos < 0.0))
628 continue;
629 this.yticks.push([pos, MochiKit.Format.roundToFixed(yval, prec)]);
630 }
631 }
632 };
633
634 PlotKit.Layout.prototype._evaluateLineTicks = function() {
635 this._evaluateLineTicksForXAxis();
636 this._evaluateLineTicksForYAxis();
637 };
638
639 PlotKit.Layout.prototype._evaluateBarTicks = function() {
640 this._evaluateLineTicks();
641 var centerInBar = function(tick) {
642 return [tick[0] + (this.minxdelta * this.xscale)/2, tick[1]];
643 };
644 this.xticks = MochiKit.Base.map(bind(centerInBar, this), this.xticks);
645
646 if (this.options.barOrientation == "horizontal") {
647 // swap scales
648 var tempticks = this.xticks;
649 this.xticks = this.yticks;
650 this.yticks = tempticks;
651
652 // we need to invert the "yaxis" (which is now the xaxis when drawn)
653 var invert = function(tick) {
654 return [1.0 - tick[0], tick[1]];
655 }
656 this.xticks = MochiKit.Base.map(invert, this.xticks);
657 }
658 };
659
660 PlotKit.Layout.prototype._evaluatePieTicks = function() {
661 var isNil = MochiKit.Base.isUndefinedOrNull;
662 var formatter = MochiKit.Format.numberFormatter("#%");
663
664 this.xticks = new Array();
665 if (this.options.xTicks) {
666 // make a lookup dict for x->slice values
667 var lookup = new Array();
668 for (var i = 0; i < this.slices.length; i++) {
669 lookup[this.slices[i].xval] = this.slices[i];
670 }
671
672 for (var i =0; i < this.options.xTicks.length; i++) {
673 var tick = this.options.xTicks[i];
674 var slice = lookup[tick.v];
675 var label = tick.label;
676 if (slice) {
677 if (isNil(label))
678 label = tick.v.toString();
679 label += " (" + formatter(slice.fraction) + ")";
680 this.xticks.push([tick.v, label]);
681 }
682 }
683 }
684 else {
685 // we make our own labels from all the slices
686 for (var i =0; i < this.slices.length; i++) {
687 var slice = this.slices[i];
688 var label = slice.xval + " (" + formatter(slice.fraction ) + ")";
689 this.xticks.push([slice.xval, label]);
690 }
691 }
692 };
693
694 PlotKit.Layout.prototype._regenerateHitTestCache = function() {
695 this.hitTestCache.xvalues = this._uniqueXValues();
696 this.hitTestCache.xlookup = new Array();
697 this.hitTestCache.x2maxy = new Array();
698
699 var listMax = MochiKit.Base.listMax;
700 var itemgetter = MochiKit.Base.itemgetter;
701 var map = MochiKit.Base.map;
702
703 // generate a lookup table for x values to y values
704 var setNames = keys(this.datasets);
705 for (var i = 0; i < setNames.length; i++) {
706 var dataset = this.datasets[setNames[i]];
707 for (var j = 0; j < dataset.length; j++) {
708 var xval = dataset[j][0];
709 var yval = dataset[j][1];
710 if (this.hitTestCache.xlookup[xval])
711 this.hitTestCache.xlookup[xval].push([yval, setNames[i]]);
712 else
713 this.hitTestCache.xlookup[xval] = [[yval, setNames[i]]];
714 }
715 }
716
717 for (var x in this.hitTestCache.xlookup) {
718 var yvals = this.hitTestCache.xlookup[x];
719 this.hitTestCache.x2maxy[x] = listMax(map(itemgetter(0), yvals));
720 }
721
722
723 };
724
725 // --------------------------------------------------------------------
726 // END Internal Functions
727 // --------------------------------------------------------------------
728
729
730 // Namespace Iniitialisation
731
732 PlotKit.LayoutModule = {};
733 PlotKit.LayoutModule.Layout = PlotKit.Layout;
734
735 PlotKit.LayoutModule.EXPORT = [
736 "Layout"
737 ];
738
739 PlotKit.LayoutModule.EXPORT_OK = [];
740
741 PlotKit.LayoutModule.__new__ = function() {
742 var m = MochiKit.Base;
743
744 m.nameFunctions(this);
745
746 this.EXPORT_TAGS = {
747 ":common": this.EXPORT,
748 ":all": m.concat(this.EXPORT, this.EXPORT_OK)
749 };
750 };
751
752 PlotKit.LayoutModule.__new__();
753 MochiKit.Base._exportSymbols(this, PlotKit.LayoutModule);
OLDNEW
« no previous file with comments | « static/js/plotkit/LICENSE ('k') | static/js/plotkit/README.chromium » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698