@@ -65,11 +65,30 @@ defaults._set('scale', {
6565 }
6666} ) ;
6767
68+ function sample ( arr , size ) {
69+ var shuffled = arr . slice ( 0 ) ;
70+ var i = arr . length ;
71+ var min = i - size ;
72+ var tmp , index ;
73+
74+ if ( size >= min ) {
75+ return arr ;
76+ }
77+
78+ while ( i -- > min ) {
79+ index = Math . floor ( ( i + 1 ) * Math . random ( ) ) ;
80+ tmp = shuffled [ index ] ;
81+ shuffled [ index ] = shuffled [ i ] ;
82+ shuffled [ i ] = tmp ;
83+ }
84+ return shuffled . slice ( min ) ;
85+ }
86+
6887function getPixelForGridLine ( scale , index , offsetGridLines ) {
6988 var lineValue = scale . getPixelForTick ( index ) ;
7089
7190 if ( offsetGridLines ) {
72- if ( scale . getTicks ( ) . length === 1 ) {
91+ if ( scale . _ticks . length === 1 ) {
7392 lineValue -= scale . isHorizontal ( ) ?
7493 Math . max ( lineValue - scale . left , scale . right - lineValue ) :
7594 Math . max ( lineValue - scale . top , scale . bottom - lineValue ) ;
@@ -304,43 +323,39 @@ var Scale = Element.extend({
304323 ticks = [ ] ;
305324 for ( i = 0 , ilen = me . ticks . length ; i < ilen ; ++ i ) {
306325 ticks . push ( {
307- index : i ,
326+ _index : i ,
308327 value : me . ticks [ i ] ,
309328 major : false
310329 } ) ;
311330 }
312331 }
313332 me . _numTicks = ticks . length ;
314333
315- me . beforeTickToLabelConversion ( ) ;
316-
317- // New implementations should return the formatted tick labels but for BACKWARD
318- // COMPAT, we still support no return (`this.ticks` internally changed by calling
319- // this method and supposed to contain only string values).
320- labels = me . convertTicksToLabels ( ticks ) || me . ticks ;
321-
322- me . afterTickToLabelConversion ( ) ;
323-
324- // IMPORTANT: below this point, we consider that `this.ticks` will NEVER change!
325- me . ticks = labels ; // BACKWARD COMPATIBILITY
326-
327- // BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`)
328- for ( i = 0 , ilen = labels . length ; i < ilen ; ++ i ) {
329- ticks [ i ] . label = labels [ i ] ;
330- }
334+ // Compute tick rotation and fit using a sampled sub-set of labels
335+ // We generally don't need to compute the size of every single label for determining scale size
336+ me . _ticks = sample ( ticks , tickOpts . sampleSize || ticks . length ) ;
331337
332- me . _ticks = ticks ;
338+ labels = me . _convertTicksToLabels ( me . _ticks ) ;
333339
334- // Tick Rotation
335340 me . beforeCalculateTickRotation ( ) ;
336341 me . calculateTickRotation ( ) ;
337342 me . afterCalculateTickRotation ( ) ;
338- // Fit
343+
339344 me . beforeFit ( ) ;
340345 me . fit ( ) ;
341346 me . afterFit ( ) ;
347+
342348 // Auto-skip
343- me . _ticks = tickOpts . display && tickOpts . autoSkip ? me . _autoSkip ( me . getTicks ( ) ) : me . getTicks ( ) ;
349+ me . _ticks = ticks = tickOpts . display && tickOpts . autoSkip ? me . _autoSkip ( ticks ) : ticks ;
350+
351+ if ( tickOpts . sampleSize ) {
352+ // Generate labels using all non-skipped ticks
353+ labels = me . _convertTicksToLabels ( ticks ) ;
354+ }
355+
356+ me . ticks = labels ; // BACKWARD COMPATIBILITY
357+
358+ // IMPORTANT: after this point, we consider that `this.ticks` will NEVER change!
344359
345360 me . afterUpdate ( ) ;
346361
@@ -524,7 +539,7 @@ var Scale = Element.extend({
524539 minSize . height = Math . min ( me . maxHeight , minSize . height + labelHeight + tickPadding ) ;
525540
526541 var offsetLeft = me . getPixelForTick ( 0 ) - me . left ;
527- var offsetRight = me . right - me . getPixelForTick ( me . _numTicks - 1 ) ;
542+ var offsetRight = me . right - me . getPixelForTick ( me . _ticks . length - 1 ) ;
528543 var paddingLeft , paddingRight ;
529544
530545 // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned
@@ -621,6 +636,31 @@ var Scale = Element.extend({
621636 return rawValue ;
622637 } ,
623638
639+ _convertTicksToLabels : function ( ticks ) {
640+ var me = this ;
641+ var labels , i , ilen ;
642+
643+ me . ticks = ticks . map ( function ( tick ) {
644+ return tick . value ;
645+ } ) ;
646+
647+ me . beforeTickToLabelConversion ( ) ;
648+
649+ // New implementations should return the formatted tick labels but for BACKWARD
650+ // COMPAT, we still support no return (`this.ticks` internally changed by calling
651+ // this method and supposed to contain only string values).
652+ labels = me . convertTicksToLabels ( ticks ) || me . ticks ;
653+
654+ me . afterTickToLabelConversion ( ) ;
655+
656+ // BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`)
657+ for ( i = 0 , ilen = labels . length ; i < ilen ; ++ i ) {
658+ ticks [ i ] . label = labels [ i ] ;
659+ }
660+
661+ return labels ;
662+ } ,
663+
624664 /**
625665 * @private
626666 */
@@ -705,7 +745,7 @@ var Scale = Element.extend({
705745 getPixelForTick : function ( index ) {
706746 var me = this ;
707747 var offset = me . options . offset ;
708- var numTicks = me . _numTicks ;
748+ var numTicks = me . _ticks . length ;
709749 if ( index < 0 || index > numTicks - 1 ) {
710750 return null ;
711751 }
@@ -894,8 +934,8 @@ var Scale = Element.extend({
894934 tickEnd = me . left + tl ;
895935 }
896936
897- helpers . each ( ticks , function ( tick ) {
898- var index = tick . index ;
937+ helpers . each ( ticks , function ( tick , i ) {
938+ var index = tick . _index ;
899939 var label = tick . label ;
900940 var tickFont = tick . major ? tickFonts . major : tickFonts . minor ;
901941 var lineHeight = tickFont . lineHeight ;
@@ -916,7 +956,7 @@ var Scale = Element.extend({
916956 // Common properties
917957 var tx1 , ty1 , tx2 , ty2 , x1 , y1 , x2 , y2 , labelX , labelY , textOffset , textAlign ;
918958 var labelCount = helpers . isArray ( label ) ? label . length : 1 ;
919- var lineValue = getPixelForGridLine ( me , index , gridLines . offsetGridLines ) ;
959+ var lineValue = getPixelForGridLine ( me , i , gridLines . offsetGridLines ) ;
920960
921961 if ( isHorizontal ) {
922962 var labelYOffset = tl + tickPadding ;
@@ -928,7 +968,7 @@ var Scale = Element.extend({
928968 tx1 = tx2 = x1 = x2 = alignPixel ( chart , lineValue , lineWidth ) ;
929969 ty1 = tickStart ;
930970 ty2 = tickEnd ;
931- labelX = me . getPixelForTick ( index ) + labelOffset ; // x values for optionTicks (need to consider offsetLabel option)
971+ labelX = me . getPixelForTick ( i ) + labelOffset ; // x values for optionTicks (need to consider offsetLabel option)
932972
933973 if ( position === 'top' ) {
934974 y1 = alignPixel ( chart , chartArea . top , axisWidth ) + axisWidth / 2 ;
@@ -953,7 +993,7 @@ var Scale = Element.extend({
953993 tx1 = tickStart ;
954994 tx2 = tickEnd ;
955995 ty1 = ty2 = y1 = y2 = alignPixel ( chart , lineValue , lineWidth ) ;
956- labelY = me . getPixelForTick ( index ) + labelOffset ;
996+ labelY = me . getPixelForTick ( i ) + labelOffset ;
957997 textOffset = ( 1 - labelCount ) * lineHeight / 2 ;
958998
959999 if ( position === 'left' ) {
0 commit comments