@@ -33,22 +33,38 @@ module.exports = function calc(gd, trace) {
3333 trace . orientation === 'h' ? ( trace . yaxis || 'y' ) : ( trace . xaxis || 'x' ) ) ,
3434 maindata = trace . orientation === 'h' ? 'y' : 'x' ,
3535 counterdata = { x : 'y' , y : 'x' } [ maindata ] ,
36- calendar = trace [ maindata + 'calendar' ] ;
36+ calendar = trace [ maindata + 'calendar' ] ,
37+ cumulativeSpec = trace . cumulative ;
3738
3839 cleanBins ( trace , pa , maindata ) ;
3940
4041 // prepare the raw data
4142 var pos0 = pa . makeCalcdata ( trace , maindata ) ;
43+
4244 // calculate the bins
43- if ( ( trace [ 'autobin' + maindata ] !== false ) || ! ( maindata + 'bins' in trace ) ) {
44- trace [ maindata + 'bins' ] = Axes . autoBin ( pos0 , pa , trace [ 'nbins' + maindata ] , false , calendar ) ;
45+ var binAttr = maindata + 'bins' ,
46+ binspec ;
47+ if ( ( trace [ 'autobin' + maindata ] !== false ) || ! ( binAttr in trace ) ) {
48+ binspec = Axes . autoBin ( pos0 , pa , trace [ 'nbins' + maindata ] , false , calendar ) ;
49+
50+ // adjust for CDF edge cases
51+ if ( cumulativeSpec . enabled && ( cumulativeSpec . currentbin !== 'include' ) ) {
52+ if ( cumulativeSpec . direction === 'decreasing' ) {
53+ binspec . start = pa . c2r ( pa . r2c ( binspec . start ) - binspec . size ) ;
54+ }
55+ else {
56+ binspec . end = pa . c2r ( pa . r2c ( binspec . end ) + binspec . size ) ;
57+ }
58+ }
4559
46- // copy bin info back to the source data.
47- trace . _input [ maindata + 'bins' ] = trace [ maindata + 'bins' ] ;
60+ // copy bin info back to the source and full data.
61+ trace . _input [ binAttr ] = trace [ binAttr ] = binspec ;
62+ }
63+ else {
64+ binspec = trace [ binAttr ] ;
4865 }
4966
50- var binspec = trace [ maindata + 'bins' ] ,
51- nonuniformBins = typeof binspec . size === 'string' ,
67+ var nonuniformBins = typeof binspec . size === 'string' ,
5268 bins = nonuniformBins ? [ ] : binspec ,
5369 // make the empty bin array
5470 i2 ,
@@ -59,8 +75,16 @@ module.exports = function calc(gd, trace) {
5975 total = 0 ,
6076 norm = trace . histnorm ,
6177 func = trace . histfunc ,
62- densitynorm = norm . indexOf ( 'density' ) !== - 1 ,
63- extremefunc = func === 'max' || func === 'min' ,
78+ densitynorm = norm . indexOf ( 'density' ) !== - 1 ;
79+
80+ if ( cumulativeSpec . enabled && densitynorm ) {
81+ // we treat "cumulative" like it means "integral" if you use a density norm,
82+ // which in the end means it's the same as without "density"
83+ norm = norm . replace ( / ? d e n s i t y $ / , '' ) ;
84+ densitynorm = false ;
85+ }
86+
87+ var extremefunc = func === 'max' || func === 'min' ,
6488 sizeinit = extremefunc ? null : 0 ,
6589 binfunc = binFunctions . count ,
6690 normfunc = normFunctions [ norm ] ,
@@ -115,6 +139,10 @@ module.exports = function calc(gd, trace) {
115139 if ( doavg ) total = doAvg ( size , counts ) ;
116140 if ( normfunc ) normfunc ( size , total , inc ) ;
117141
142+ // after all normalization etc, now we can accumulate if desired
143+ if ( cumulativeSpec . enabled ) cdf ( size , cumulativeSpec . direction , cumulativeSpec . currentbin ) ;
144+
145+
118146 var serieslen = Math . min ( pos . length , size . length ) ,
119147 cd = [ ] ,
120148 firstNonzero = 0 ,
@@ -142,3 +170,57 @@ module.exports = function calc(gd, trace) {
142170
143171 return cd ;
144172} ;
173+
174+ function cdf ( size , direction , currentbin ) {
175+ var i ,
176+ vi ,
177+ prevSum ;
178+
179+ function firstHalfPoint ( i ) {
180+ prevSum = size [ i ] ;
181+ size [ i ] /= 2 ;
182+ }
183+
184+ function nextHalfPoint ( i ) {
185+ vi = size [ i ] ;
186+ size [ i ] = prevSum + vi / 2 ;
187+ prevSum += vi ;
188+ }
189+
190+ if ( currentbin === 'half' ) {
191+
192+ if ( direction === 'increasing' ) {
193+ firstHalfPoint ( 0 ) ;
194+ for ( i = 1 ; i < size . length ; i ++ ) {
195+ nextHalfPoint ( i ) ;
196+ }
197+ }
198+ else {
199+ firstHalfPoint ( size . length - 1 ) ;
200+ for ( i = size . length - 2 ; i >= 0 ; i -- ) {
201+ nextHalfPoint ( i ) ;
202+ }
203+ }
204+ }
205+ else if ( direction === 'increasing' ) {
206+ for ( i = 1 ; i < size . length ; i ++ ) {
207+ size [ i ] += size [ i - 1 ] ;
208+ }
209+
210+ // 'exclude' is identical to 'include' just shifted one bin over
211+ if ( currentbin === 'exclude' ) {
212+ size . unshift ( 0 ) ;
213+ size . pop ( ) ;
214+ }
215+ }
216+ else {
217+ for ( i = size . length - 2 ; i >= 0 ; i -- ) {
218+ size [ i ] += size [ i + 1 ] ;
219+ }
220+
221+ if ( currentbin === 'exclude' ) {
222+ size . push ( 0 ) ;
223+ size . shift ( ) ;
224+ }
225+ }
226+ }
0 commit comments