X-Git-Url: https://adrianiainlam.tk/git/?a=blobdiff_plain;f=plotkit_v091%2FPlotKit%2FLayout.js;h=d9bcd6af2889d14a8b8476cc1bbf5817e246ea49;hb=73a464171469a18a7f2cc55a98762c4b31e3754f;hp=ff1c9db8acd7f2ea0784b89c932129c8a306bbe9;hpb=6a1aa64f6d22473e0357ad1cd7bd93259d899a69;p=dygraphs.git diff --git a/plotkit_v091/PlotKit/Layout.js b/plotkit_v091/PlotKit/Layout.js index ff1c9db..d9bcd6a 100644 --- a/plotkit_v091/PlotKit/Layout.js +++ b/plotkit_v091/PlotKit/Layout.js @@ -124,40 +124,6 @@ PlotKit.Layout.prototype.removeDataset = function(setname, set_xy) { delete this.datasets[setname]; }; -PlotKit.Layout.prototype.addDatasetFromTable = function(name, tableElement, xcol, ycol, lcol) { - var isNil = MochiKit.Base.isUndefinedOrNull; - var scrapeText = MochiKit.DOM.scrapeText; - var strip = MochiKit.Format.strip; - - if (isNil(xcol)) - xcol = 0; - if (isNil(ycol)) - ycol = 1; - if (isNil(lcol)) - lcol = -1; - - var rows = tableElement.tBodies[0].rows; - var data = new Array(); - var labels = new Array(); - - if (!isNil(rows)) { - for (var i = 0; i < rows.length; i++) { - data.push([parseFloat(strip(scrapeText(rows[i].cells[xcol]))), - parseFloat(strip(scrapeText(rows[i].cells[ycol])))]); - if (lcol >= 0){ - labels.push({v: parseFloat(strip(scrapeText(rows[i].cells[xcol]))), - label: strip(scrapeText(rows[i].cells[lcol]))}); - } - } - this.addDataset(name, data); - if (lcol >= 0) { - this.options.xTicks = labels; - } - return true; - } - return false; -}; - // -------------------------------------------------------------------- // Evaluates the layout for the current data and style. // -------------------------------------------------------------------- @@ -165,110 +131,14 @@ PlotKit.Layout.prototype.addDatasetFromTable = function(name, tableElement, xcol PlotKit.Layout.prototype.evaluate = function() { this._evaluateLimits(); this._evaluateScales(); - if (this.style == "bar") { - if (this.options.barOrientation == "horizontal") { - this._evaluateHorizBarCharts(); - } - else { - this._evaluateBarCharts(); - } - this._evaluateBarTicks(); - } - else if (this.style == "line") { + if (this.style == "line") { this._evaluateLineCharts(); this._evaluateLineTicks(); } - else if (this.style == "pie") { - this._evaluatePieCharts(); - this._evaluatePieTicks(); - } }; -// Given the fractional x, y positions, report the corresponding -// x, y values. -PlotKit.Layout.prototype.hitTest = function(x, y) { - // TODO: make this more efficient with better datastructures - // for this.bars, this.points and this.slices - - var f = MochiKit.Format.twoDigitFloat; - - if ((this.style == "bar") && this.bars && (this.bars.length > 0)) { - for (var i = 0; i < this.bars.length; i++) { - var bar = this.bars[i]; - if ((x >= bar.x) && (x <= bar.x + bar.w) - && (y >= bar.y) && (y - bar.y <= bar.h)) - return bar; - } - } - - else if (this.style == "line") { - if (this.hitTestCache.x2maxy == null) { - this._regenerateHitTestCache(); - } - - // 1. find the xvalues that equal or closest to the give x - var xval = x / this.xscale; - var xvalues = this.hitTestCache.xvalues; - var xbefore = null; - var xafter = null; - - for (var i = 1; i < xvalues.length; i++) { - if (xvalues[i] > xval) { - xbefore = xvalues[i-1]; - xafter = xvalues[i]; - break; - } - } - - if ((xbefore != null)) { - var ybefore = this.hitTestCache.x2maxy[xbefore]; - var yafter = this.hitTestCache.x2maxy[xafter]; - var yval = (1.0 - y)/this.yscale; - - // interpolate whether we will fall inside or outside - var gradient = (yafter - ybefore) / (xafter - xbefore); - var projmaxy = ybefore + gradient * (xval - xbefore); - if (projmaxy >= yval) { - // inside the highest curve (roughly) - var obj = {xval: xval, yval: yval, - xafter: xafter, yafter: yafter, - xbefore: xbefore, ybefore: ybefore, - yprojected: projmaxy - }; - return obj; - } - } - } - - else if (this.style == "pie") { - var dist = Math.sqrt((y-0.5)*(y-0.5) + (x-0.5)*(x-0.5)); - if (dist > this.options.pieRadius) - return null; - - // TODO: actually doesn't work if we don't know how the Canvas - // lays it out, need to fix! - var angle = Math.atan2(y - 0.5, x - 0.5) - Math.PI/2; - for (var i = 0; i < this.slices.length; i++) { - var slice = this.slices[i]; - if (slice.startAngle < angle && slice.endAngle >= angle) - return slice; - } - } - - return null; -}; - -// Reports valid position rectangle for X value (only valid for bar charts) -PlotKit.Layout.prototype.rectForX = function(x) { - return null; -}; - -// Reports valid angles through which X value encloses (only valid for pie charts) -PlotKit.Layout.prototype.angleRangeForX = function(x) { - return null; -}; // -------------------------------------------------------------------- // START Internal Functions @@ -344,145 +214,6 @@ PlotKit.Layout.prototype._uniqueXValues = function() { return uniq(xvalues); }; -// Create the bars -PlotKit.Layout.prototype._evaluateBarCharts = function() { - var items = PlotKit.Base.items; - - var setCount = items(this.datasets).length; - - // work out how far separated values are - var xdelta = 10000000; - var xvalues = this._uniqueXValues(); - for (var i = 1; i < xvalues.length; i++) { - xdelta = Math.min(Math.abs(xvalues[i] - xvalues[i-1]), xdelta); - } - - var barWidth = 0; - var barWidthForSet = 0; - var barMargin = 0; - if (xvalues.length == 1) { - // note we have to do something smarter if we only plot one value - xdelta = 1.0; - this.xscale = 1.0; - this.minxval = xvalues[0]; - barWidth = 1.0 * this.options.barWidthFillFraction; - barWidthForSet = barWidth/setCount; - barMargin = (1.0 - this.options.barWidthFillFraction)/2; - } - else { - // readjust xscale to fix with bar charts - if (this.xrange == 1) { - this.xscale = 0.5; - } - else if (this.xrange == 2) { - this.xscale = 1/3.0; - } - else { - this.xscale = (1.0 - xdelta/this.xrange)/this.xrange; - } - barWidth = xdelta * this.xscale * this.options.barWidthFillFraction; - barWidthForSet = barWidth / setCount; - barMargin = xdelta * this.xscale * (1.0 - this.options.barWidthFillFraction)/2; - } - - this.minxdelta = xdelta; // need this for tick positions - - // add all the rects - this.bars = new Array(); - var i = 0; - for (var setName in this.datasets) { - var dataset = this.datasets[setName]; - if (PlotKit.Base.isFuncLike(dataset)) continue; - for (var j = 0; j < dataset.length; j++) { - var item = dataset[j]; - var rect = { - x: ((parseFloat(item[0]) - this.minxval) * this.xscale) + (i * barWidthForSet) + barMargin, - y: 1.0 - ((parseFloat(item[1]) - this.minyval) * this.yscale), - w: barWidthForSet, - h: ((parseFloat(item[1]) - this.minyval) * this.yscale), - xval: parseFloat(item[0]), - yval: parseFloat(item[1]), - name: setName - }; - if ((rect.x >= 0.0) && (rect.x <= 1.0) && - (rect.y >= 0.0) && (rect.y <= 1.0)) { - this.bars.push(rect); - } - } - i++; - } -}; - -// Create the horizontal bars -PlotKit.Layout.prototype._evaluateHorizBarCharts = function() { - var items = PlotKit.Base.items; - - var setCount = items(this.datasets).length; - - // work out how far separated values are - var xdelta = 10000000; - var xvalues = this._uniqueXValues(); - for (var i = 1; i < xvalues.length; i++) { - xdelta = Math.min(Math.abs(xvalues[i] - xvalues[i-1]), xdelta); - } - - var barWidth = 0; - var barWidthForSet = 0; - var barMargin = 0; - - // work out how far each far each bar is separated - if (xvalues.length == 1) { - // do something smarter if we only plot one value - xdelta = 1.0; - this.xscale = 1.0; - this.minxval = xvalues[0]; - barWidth = 1.0 * this.options.barWidthFillFraction; - barWidthForSet = barWidth/setCount; - barMargin = (1.0 - this.options.barWidthFillFraction)/2; - } - else { - // readjust yscale to fix with bar charts - this.xscale = (1.0 - xdelta/this.xrange)/this.xrange; - barWidth = xdelta * this.xscale * this.options.barWidthFillFraction; - barWidthForSet = barWidth / setCount; - barMargin = xdelta * this.xscale * (1.0 - this.options.barWidthFillFraction)/2; - } - - this.minxdelta = xdelta; // need this for tick positions - - // add all the rects - this.bars = new Array(); - var i = 0; - for (var setName in this.datasets) { - var dataset = this.datasets[setName]; - if (PlotKit.Base.isFuncLike(dataset)) continue; - for (var j = 0; j < dataset.length; j++) { - var item = dataset[j]; - var rect = { - y: ((parseFloat(item[0]) - this.minxval) * this.xscale) + (i * barWidthForSet) + barMargin, - x: 0.0, - h: barWidthForSet, - w: ((parseFloat(item[1]) - this.minyval) * this.yscale), - xval: parseFloat(item[0]), - yval: parseFloat(item[1]), - name: setName - }; - - // limit the x, y values so they do not overdraw - if (rect.y <= 0.0) { - rect.y = 0.0; - } - if (rect.y >= 1.0) { - rect.y = 1.0; - } - if ((rect.x >= 0.0) && (rect.x <= 1.0)) { - this.bars.push(rect); - } - } - i++; - } -}; - // Create the line charts PlotKit.Layout.prototype._evaluateLineCharts = function() { @@ -522,37 +253,6 @@ PlotKit.Layout.prototype._evaluateLineCharts = function() { } }; -// Create the pie charts -PlotKit.Layout.prototype._evaluatePieCharts = function() { - var items = PlotKit.Base.items; - var sum = MochiKit.Iter.sum; - var getter = MochiKit.Base.itemgetter; - - var setCount = items(this.datasets).length; - - // we plot the y values of the first dataset - var dataset = items(this.datasets)[0][1]; - var total = sum(map(getter(1), dataset)); - - this.slices = new Array(); - var currentAngle = 0.0; - for (var i = 0; i < dataset.length; i++) { - var fraction = dataset[i][1] / total; - var startAngle = currentAngle * Math.PI * 2; - var endAngle = (currentAngle + fraction) * Math.PI * 2; - - var slice = {fraction: fraction, - xval: dataset[i][0], - yval: dataset[i][1], - startAngle: startAngle, - endAngle: endAngle - }; - if (dataset[i][1] != 0) { - this.slices.push(slice); - } - currentAngle += fraction; - } -}; PlotKit.Layout.prototype._evaluateLineTicksForXAxis = function() { var isNil = MochiKit.Base.isUndefinedOrNull; @@ -610,26 +310,6 @@ PlotKit.Layout.prototype._evaluateLineTicksForYAxis = function() { }; MochiKit.Iter.forEach(this.options.yTicks, bind(makeTicks, this)); } - else if (this.options.yNumberOfTicks) { - // We use the optionally defined number of ticks as a guide - this.yticks = new Array(); - - // if we get this separation right, we'll have good looking graphs - var roundInt = PlotKit.Base.roundInterval; - var prec = this.options.yTickPrecision; - var roughSeparation = roundInt(this.yrange, - this.options.yNumberOfTicks, prec); - - // round off each value of the y-axis to the precision - // eg. 1.3333 at precision 1 -> 1.3 - for (var i = 0; i <= this.options.yNumberOfTicks; i++) { - var yval = this.minyval + (i * roughSeparation); - var pos = 1.0 - ((yval - this.minyval) * this.yscale); - if ((pos > 1.0) || (pos < 0.0)) - continue; - this.yticks.push([pos, MochiKit.Format.roundToFixed(yval, prec)]); - } - } }; PlotKit.Layout.prototype._evaluateLineTicks = function() { @@ -637,91 +317,6 @@ PlotKit.Layout.prototype._evaluateLineTicks = function() { this._evaluateLineTicksForYAxis(); }; -PlotKit.Layout.prototype._evaluateBarTicks = function() { - this._evaluateLineTicks(); - var centerInBar = function(tick) { - return [tick[0] + (this.minxdelta * this.xscale)/2, tick[1]]; - }; - this.xticks = MochiKit.Base.map(bind(centerInBar, this), this.xticks); - - if (this.options.barOrientation == "horizontal") { - // swap scales - var tempticks = this.xticks; - this.xticks = this.yticks; - this.yticks = tempticks; - - // we need to invert the "yaxis" (which is now the xaxis when drawn) - var invert = function(tick) { - return [1.0 - tick[0], tick[1]]; - } - this.xticks = MochiKit.Base.map(invert, this.xticks); - } -}; - -PlotKit.Layout.prototype._evaluatePieTicks = function() { - var isNil = MochiKit.Base.isUndefinedOrNull; - var formatter = MochiKit.Format.numberFormatter("#%"); - - this.xticks = new Array(); - if (this.options.xTicks) { - // make a lookup dict for x->slice values - var lookup = new Array(); - for (var i = 0; i < this.slices.length; i++) { - lookup[this.slices[i].xval] = this.slices[i]; - } - - for (var i =0; i < this.options.xTicks.length; i++) { - var tick = this.options.xTicks[i]; - var slice = lookup[tick.v]; - var label = tick.label; - if (slice) { - if (isNil(label)) - label = tick.v.toString(); - label += " (" + formatter(slice.fraction) + ")"; - this.xticks.push([tick.v, label]); - } - } - } - else { - // we make our own labels from all the slices - for (var i =0; i < this.slices.length; i++) { - var slice = this.slices[i]; - var label = slice.xval + " (" + formatter(slice.fraction) + ")"; - this.xticks.push([slice.xval, label]); - } - } -}; - -PlotKit.Layout.prototype._regenerateHitTestCache = function() { - this.hitTestCache.xvalues = this._uniqueXValues(); - this.hitTestCache.xlookup = new Array(); - this.hitTestCache.x2maxy = new Array(); - - var listMax = MochiKit.Base.listMax; - var itemgetter = MochiKit.Base.itemgetter; - var map = MochiKit.Base.map; - - // generate a lookup table for x values to y values - var setNames = keys(this.datasets); - for (var i = 0; i < setNames.length; i++) { - var dataset = this.datasets[setNames[i]]; - for (var j = 0; j < dataset.length; j++) { - var xval = dataset[j][0]; - var yval = dataset[j][1]; - if (this.hitTestCache.xlookup[xval]) - this.hitTestCache.xlookup[xval].push([yval, setNames[i]]); - else - this.hitTestCache.xlookup[xval] = [[yval, setNames[i]]]; - } - } - - for (var x in this.hitTestCache.xlookup) { - var yvals = this.hitTestCache.xlookup[x]; - this.hitTestCache.x2maxy[x] = listMax(map(itemgetter(0), yvals)); - } - - -}; // -------------------------------------------------------------------- // END Internal Functions