|
| 1 | +/** |
| 2 | +* Copyright 2012-2015, Plotly, Inc. |
| 3 | +* All rights reserved. |
| 4 | +* |
| 5 | +* This source code is licensed under the MIT license found in the |
| 6 | +* LICENSE file in the root directory of this source tree. |
| 7 | +*/ |
| 8 | + |
| 9 | + |
| 10 | +'use strict'; |
| 11 | + |
| 12 | +var Plotly = require('../../plotly'); |
| 13 | +var Lib = require('../../lib'); |
| 14 | + |
| 15 | +var binFunctions = require('../histogram/bin_functions'); |
| 16 | +var normFunctions = require('../histogram/norm_functions'); |
| 17 | +var doAvg = require('../histogram/average'); |
| 18 | + |
| 19 | + |
| 20 | +module.exports = function calc(gd, trace) { |
| 21 | + var xa = Plotly.Axes.getFromId(gd, trace.xaxis||'x'), |
| 22 | + x = trace.x ? xa.makeCalcdata(trace, 'x') : [], |
| 23 | + ya = Plotly.Axes.getFromId(gd, trace.yaxis||'y'), |
| 24 | + y = trace.y ? ya.makeCalcdata(trace, 'y') : [], |
| 25 | + x0, |
| 26 | + dx, |
| 27 | + y0, |
| 28 | + dy, |
| 29 | + z, |
| 30 | + i; |
| 31 | + |
| 32 | + var serieslen = Math.min(x.length, y.length); |
| 33 | + if(x.length>serieslen) x.splice(serieslen, x.length-serieslen); |
| 34 | + if(y.length>serieslen) y.splice(serieslen, y.length-serieslen); |
| 35 | + |
| 36 | + Lib.markTime('done convert data'); |
| 37 | + |
| 38 | + // calculate the bins |
| 39 | + if(trace.autobinx || !('xbins' in trace)) { |
| 40 | + trace.xbins = Plotly.Axes.autoBin(x, xa, trace.nbinsx, '2d'); |
| 41 | + if(trace.type==='histogram2dcontour') { |
| 42 | + trace.xbins.start -= trace.xbins.size; |
| 43 | + trace.xbins.end += trace.xbins.size; |
| 44 | + } |
| 45 | + |
| 46 | + // copy bin info back to the source data. |
| 47 | + trace._input.xbins = trace.xbins; |
| 48 | + } |
| 49 | + if(trace.autobiny || !('ybins' in trace)) { |
| 50 | + trace.ybins = Plotly.Axes.autoBin(y,ya,trace.nbinsy,'2d'); |
| 51 | + if(trace.type==='histogram2dcontour') { |
| 52 | + trace.ybins.start -= trace.ybins.size; |
| 53 | + trace.ybins.end += trace.ybins.size; |
| 54 | + } |
| 55 | + trace._input.ybins = trace.ybins; |
| 56 | + } |
| 57 | + Lib.markTime('done autoBin'); |
| 58 | + |
| 59 | + // make the empty bin array & scale the map |
| 60 | + z = []; |
| 61 | + var onecol = [], |
| 62 | + zerocol = [], |
| 63 | + xbins = (typeof(trace.xbins.size)==='string') ? [] : trace.xbins, |
| 64 | + ybins = (typeof(trace.xbins.size)==='string') ? [] : trace.ybins, |
| 65 | + total = 0, |
| 66 | + n, |
| 67 | + m, |
| 68 | + counts=[], |
| 69 | + norm = trace.histnorm, |
| 70 | + func = trace.histfunc, |
| 71 | + densitynorm = (norm.indexOf('density')!==-1), |
| 72 | + extremefunc = (func==='max' || func==='min'), |
| 73 | + sizeinit = (extremefunc ? null : 0), |
| 74 | + binfunc = binFunctions.count, |
| 75 | + normfunc = normFunctions[norm], |
| 76 | + doavg = false, |
| 77 | + xinc = [], |
| 78 | + yinc = []; |
| 79 | + |
| 80 | + // set a binning function other than count? |
| 81 | + // for binning functions: check first for 'z', |
| 82 | + // then 'mc' in case we had a colored scatter plot |
| 83 | + // and want to transfer these colors to the 2D histo |
| 84 | + // TODO: this is why we need a data picker in the popover... |
| 85 | + var rawCounterData = ('z' in trace) ? |
| 86 | + trace.z : |
| 87 | + (('marker' in trace && Array.isArray(trace.marker.color)) ? |
| 88 | + trace.marker.color : ''); |
| 89 | + if(rawCounterData && func!=='count') { |
| 90 | + doavg = func==='avg'; |
| 91 | + binfunc = binFunctions[func]; |
| 92 | + } |
| 93 | + |
| 94 | + // decrease end a little in case of rounding errors |
| 95 | + var binspec = trace.xbins, |
| 96 | + binend = binspec.end + |
| 97 | + (binspec.start - Plotly.Axes.tickIncrement(binspec.start, binspec.size)) / 1e6; |
| 98 | + |
| 99 | + for(i=binspec.start; i<binend; |
| 100 | + i=Plotly.Axes.tickIncrement(i,binspec.size)) { |
| 101 | + onecol.push(sizeinit); |
| 102 | + if(Array.isArray(xbins)) xbins.push(i); |
| 103 | + if(doavg) zerocol.push(0); |
| 104 | + } |
| 105 | + if(Array.isArray(xbins)) xbins.push(i); |
| 106 | + |
| 107 | + var nx = onecol.length; |
| 108 | + x0 = trace.xbins.start; |
| 109 | + dx = (i-x0)/nx; |
| 110 | + x0 += dx/2; |
| 111 | + |
| 112 | + binspec = trace.ybins; |
| 113 | + binend = binspec.end + |
| 114 | + (binspec.start - Plotly.Axes.tickIncrement(binspec.start, binspec.size)) / 1e6; |
| 115 | + |
| 116 | + for(i=binspec.start; i<binend; |
| 117 | + i=Plotly.Axes.tickIncrement(i,binspec.size)) { |
| 118 | + z.push(onecol.concat()); |
| 119 | + if(Array.isArray(ybins)) ybins.push(i); |
| 120 | + if(doavg) counts.push(zerocol.concat()); |
| 121 | + } |
| 122 | + if(Array.isArray(ybins)) ybins.push(i); |
| 123 | + |
| 124 | + var ny = z.length; |
| 125 | + y0 = trace.ybins.start; |
| 126 | + dy = (i-y0)/ny; |
| 127 | + y0 += dy/2; |
| 128 | + |
| 129 | + if(densitynorm) { |
| 130 | + xinc = onecol.map(function(v,i){ |
| 131 | + if(Array.isArray(xbins)) return 1/(xbins[i+1]-xbins[i]); |
| 132 | + return 1/dx; |
| 133 | + }); |
| 134 | + yinc = z.map(function(v,i){ |
| 135 | + if(Array.isArray(ybins)) return 1/(ybins[i+1]-ybins[i]); |
| 136 | + return 1/dy; |
| 137 | + }); |
| 138 | + } |
| 139 | + |
| 140 | + |
| 141 | + Lib.markTime('done making bins'); |
| 142 | + // put data into bins |
| 143 | + for(i=0; i<serieslen; i++) { |
| 144 | + n = Lib.findBin(x[i],xbins); |
| 145 | + m = Lib.findBin(y[i],ybins); |
| 146 | + if(n>=0 && n<nx && m>=0 && m<ny) { |
| 147 | + total += binfunc(n, i, z[m], rawCounterData, counts[m]); |
| 148 | + } |
| 149 | + } |
| 150 | + // normalize, if needed |
| 151 | + if(doavg) { |
| 152 | + for(m=0; m<ny; m++) total += doAvg(z[m], counts[m]); |
| 153 | + } |
| 154 | + if(normfunc) { |
| 155 | + for(m=0; m<ny; m++) normfunc(z[m], total, xinc, yinc[m]); |
| 156 | + } |
| 157 | + Lib.markTime('done binning'); |
| 158 | + |
| 159 | + return { |
| 160 | + x: x, |
| 161 | + x0: x0, |
| 162 | + dx: dx, |
| 163 | + y: y, |
| 164 | + y0: y0, |
| 165 | + dy: dy, |
| 166 | + z: z |
| 167 | + }; |
| 168 | +}; |
0 commit comments