@@ -195,14 +195,21 @@ function concatExtremes(gd, ax) {
195195 var fullLayout = gd . _fullLayout ;
196196 var minArray = [ ] ;
197197 var maxArray = [ ] ;
198+ var i , j , d ;
198199
199200 function _concat ( cont , indices ) {
200- for ( var i = 0 ; i < indices . length ; i ++ ) {
201+ for ( i = 0 ; i < indices . length ; i ++ ) {
201202 var item = cont [ indices [ i ] ] ;
202203 var extremes = ( item . _extremes || { } ) [ axId ] ;
203204 if ( item . visible === true && extremes ) {
204- minArray = minArray . concat ( extremes . min ) ;
205- maxArray = maxArray . concat ( extremes . max ) ;
205+ for ( j = 0 ; j < extremes . min . length ; j ++ ) {
206+ d = extremes . min [ j ] ;
207+ collapseMinArray ( minArray , d . val , d . pad , { extrapad : d . extrapad } ) ;
208+ }
209+ for ( j = 0 ; j < extremes . max . length ; j ++ ) {
210+ d = extremes . max [ j ] ;
211+ collapseMaxArray ( maxArray , d . val , d . pad , { extrapad : d . extrapad } ) ;
212+ }
206213 }
207214 }
208215 }
@@ -211,8 +218,6 @@ function concatExtremes(gd, ax) {
211218 _concat ( fullLayout . annotations || [ ] , ax . _annIndices || [ ] ) ;
212219 _concat ( fullLayout . shapes || [ ] , ax . _shapeIndices || [ ] ) ;
213220
214- // TODO collapse more!
215-
216221 return { min : minArray , max : maxArray } ;
217222}
218223
@@ -295,11 +300,9 @@ function findExtremes(ax, data, options) {
295300 var len = data . length ;
296301 var extrapad = options . padded || false ;
297302 var tozero = options . tozero && ( ax . type === 'linear' || ax . type === '-' ) ;
298- var isLog = ( ax . type === 'log' ) ;
299-
300- var i , j , k , v , di , dmin , dmax , ppadiplus , ppadiminus , includeThis , vmin , vmax ;
301-
303+ var isLog = ax . type === 'log' ;
302304 var hasArrayOption = false ;
305+ var i , v , di , dmin , dmax , ppadiplus , ppadiminus , vmin , vmax ;
303306
304307 function makePadAccessor ( item ) {
305308 if ( Array . isArray ( item ) ) {
@@ -344,6 +347,8 @@ function findExtremes(ax, data, options) {
344347 len = 2 ;
345348 }
346349
350+ var collapseOpts = { tozero : tozero , extrapad : extrapad } ;
351+
347352 function addItem ( i ) {
348353 di = data [ i ] ;
349354 if ( ! isNumeric ( di ) ) return ;
@@ -364,48 +369,11 @@ function findExtremes(ax, data, options) {
364369 dmin = Math . min ( 0 , dmin ) ;
365370 dmax = Math . max ( 0 , dmax ) ;
366371 }
367-
368- for ( k = 0 ; k < 2 ; k ++ ) {
369- var newVal = k ? dmax : dmin ;
370- if ( goodNumber ( newVal ) ) {
371- var extremes = k ? maxArray : minArray ;
372- var newPad = k ? ppadiplus : ppadiminus ;
373- var atLeastAsExtreme = k ? greaterOrEqual : lessOrEqual ;
374-
375- includeThis = true ;
376- /*
377- * Take items v from ax._min/_max and compare them to the presently active point:
378- * - Since we don't yet know the relationship between pixels and values
379- * (that's what we're trying to figure out!) AND we don't yet know how
380- * many pixels `extrapad` represents (it's going to be 5% of the length,
381- * but we don't want to have to redo _min and _max just because length changed)
382- * two point must satisfy three criteria simultaneously for one to supersede the other:
383- * - at least as extreme a `val`
384- * - at least as big a `pad`
385- * - an unpadded point cannot supersede a padded point, but any other combination can
386- *
387- * - If the item supersedes the new point, set includethis false
388- * - If the new pt supersedes the item, delete it from ax._min/_max
389- */
390- for ( j = 0 ; j < extremes . length && includeThis ; j ++ ) {
391- v = extremes [ j ] ;
392- if ( atLeastAsExtreme ( v . val , newVal ) && v . pad >= newPad && ( v . extrapad || ! extrapad ) ) {
393- includeThis = false ;
394- break ;
395- } else if ( atLeastAsExtreme ( newVal , v . val ) && v . pad <= newPad && ( extrapad || ! v . extrapad ) ) {
396- extremes . splice ( j , 1 ) ;
397- j -- ;
398- }
399- }
400- if ( includeThis ) {
401- var clipAtZero = ( tozero && newVal === 0 ) ;
402- extremes . push ( {
403- val : newVal ,
404- pad : clipAtZero ? 0 : newPad ,
405- extrapad : clipAtZero ? false : extrapad
406- } ) ;
407- }
408- }
372+ if ( goodNumber ( dmin ) ) {
373+ collapseMinArray ( minArray , dmin , ppadiminus , collapseOpts ) ;
374+ }
375+ if ( goodNumber ( dmax ) ) {
376+ collapseMaxArray ( maxArray , dmax , ppadiplus , collapseOpts ) ;
409377 }
410378 }
411379
@@ -419,6 +387,76 @@ function findExtremes(ax, data, options) {
419387 return { min : minArray , max : maxArray } ;
420388}
421389
390+ function collapseMinArray ( array , newVal , newPad , opts ) {
391+ collapseArray ( array , newVal , newPad , opts , lessOrEqual ) ;
392+ }
393+
394+ function collapseMaxArray ( array , newVal , newPad , opts ) {
395+ collapseArray ( array , newVal , newPad , opts , greaterOrEqual ) ;
396+ }
397+
398+ /**
399+ * collapseArray
400+ *
401+ * Take items v from 'array' compare them to 'newVal', 'newPad'
402+ *
403+ * @param {array } array:
404+ * current set of min or max extremes
405+ * @param {number } newVal:
406+ * new value to compare against
407+ * @param {number } newPad:
408+ * pad value associated with 'newVal'
409+ * @param {object } opts:
410+ * - tozero {boolean}
411+ * - extrapad {number}
412+ * @param {function } atLeastAsExtreme:
413+ * comparison function, use
414+ * - lessOrEqual for min 'array' and
415+ * - greaterOrEqual for max 'array'
416+ *
417+ * In practice, 'array' is either
418+ * - 'extremes[ax._id].min' or
419+ * - 'extremes[ax._id].max
420+ * found in traces and layout items that affect autorange.
421+ *
422+ * Since we don't yet know the relationship between pixels and values
423+ * (that's what we're trying to figure out!) AND we don't yet know how
424+ * many pixels `extrapad` represents (it's going to be 5% of the length,
425+ * but we don't want to have to redo calc just because length changed)
426+ * two point must satisfy three criteria simultaneously for one to supersede the other:
427+ * - at least as extreme a `val`
428+ * - at least as big a `pad`
429+ * - an unpadded point cannot supersede a padded point, but any other combination can
430+ *
431+ * Then:
432+ * - If the item supersedes the new point, set includeThis false
433+ * - If the new pt supersedes the item, delete it from 'array'
434+ */
435+ function collapseArray ( array , newVal , newPad , opts , atLeastAsExtreme ) {
436+ var tozero = opts . tozero ;
437+ var extrapad = opts . extrapad ;
438+ var includeThis = true ;
439+
440+ for ( var j = 0 ; j < array . length && includeThis ; j ++ ) {
441+ var v = array [ j ] ;
442+ if ( atLeastAsExtreme ( v . val , newVal ) && v . pad >= newPad && ( v . extrapad || ! extrapad ) ) {
443+ includeThis = false ;
444+ break ;
445+ } else if ( atLeastAsExtreme ( newVal , v . val ) && v . pad <= newPad && ( extrapad || ! v . extrapad ) ) {
446+ array . splice ( j , 1 ) ;
447+ j -- ;
448+ }
449+ }
450+ if ( includeThis ) {
451+ var clipAtZero = ( tozero && newVal === 0 ) ;
452+ array . push ( {
453+ val : newVal ,
454+ pad : clipAtZero ? 0 : newPad ,
455+ extrapad : clipAtZero ? false : extrapad
456+ } ) ;
457+ }
458+ }
459+
422460// In order to stop overflow errors, don't consider points
423461// too close to the limits of js floating point
424462function goodNumber ( v ) {
0 commit comments