@@ -33,22 +33,38 @@ module.exports = function calc(gd, trace) {
33
33
trace . orientation === 'h' ? ( trace . yaxis || 'y' ) : ( trace . xaxis || 'x' ) ) ,
34
34
maindata = trace . orientation === 'h' ? 'y' : 'x' ,
35
35
counterdata = { x : 'y' , y : 'x' } [ maindata ] ,
36
- calendar = trace [ maindata + 'calendar' ] ;
36
+ calendar = trace [ maindata + 'calendar' ] ,
37
+ cumulativeSpec = trace . cumulative ;
37
38
38
39
cleanBins ( trace , pa , maindata ) ;
39
40
40
41
// prepare the raw data
41
42
var pos0 = pa . makeCalcdata ( trace , maindata ) ;
43
+
42
44
// 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
+ }
45
59
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 ] ;
48
65
}
49
66
50
- var binspec = trace [ maindata + 'bins' ] ,
51
- nonuniformBins = typeof binspec . size === 'string' ,
67
+ var nonuniformBins = typeof binspec . size === 'string' ,
52
68
bins = nonuniformBins ? [ ] : binspec ,
53
69
// make the empty bin array
54
70
i2 ,
@@ -59,8 +75,16 @@ module.exports = function calc(gd, trace) {
59
75
total = 0 ,
60
76
norm = trace . histnorm ,
61
77
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' ,
64
88
sizeinit = extremefunc ? null : 0 ,
65
89
binfunc = binFunctions . count ,
66
90
normfunc = normFunctions [ norm ] ,
@@ -115,6 +139,10 @@ module.exports = function calc(gd, trace) {
115
139
if ( doavg ) total = doAvg ( size , counts ) ;
116
140
if ( normfunc ) normfunc ( size , total , inc ) ;
117
141
142
+ // after all normalization etc, now we can accumulate if desired
143
+ if ( cumulativeSpec . enabled ) cdf ( size , cumulativeSpec . direction , cumulativeSpec . currentbin ) ;
144
+
145
+
118
146
var serieslen = Math . min ( pos . length , size . length ) ,
119
147
cd = [ ] ,
120
148
firstNonzero = 0 ,
@@ -142,3 +170,57 @@ module.exports = function calc(gd, trace) {
142
170
143
171
return cd ;
144
172
} ;
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