Skip to content

Commit

Permalink
Optimized performance by caching results for getBBox for numbers, sin…
Browse files Browse the repository at this point in the history
…ce numbers are monospaced and the same bounding box applies to any number of the same character length and the same font family and font size. Also, don't delete cached bounding box on word wrap when there is only one word. These two optimizations improved rendering times by 15% on Chrome and Firefox using our benchmark tests. Relates to #2525.
  • Loading branch information
TorsteinHonsi committed Dec 13, 2013
1 parent 2447cc6 commit 1e97bff
Show file tree
Hide file tree
Showing 25 changed files with 512 additions and 24 deletions.
32 changes: 24 additions & 8 deletions js/highcharts.src.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ var UNDEFINED,
NONE = 'none',
M = 'M',
L = 'L',
numRegex = /^[0-9]+$/,
/*
* Empirical lowest possible opacities for TRACKER_FILL
* IE6: 0.002
Expand Down Expand Up @@ -2438,9 +2439,21 @@ SVGElement.prototype = {
rotation = wrapper.rotation,
element = wrapper.element,
styles = wrapper.styles,
rad = rotation * deg2rad;
rad = rotation * deg2rad,
textStr = wrapper.textStr,
numKey;

// Since numbers are monospaced, and numerical labels appear a lot in a chart,
// we assume that a label of n characters has the same bounding box as others
// of the same length.
if (textStr === '' || numRegex.test(textStr)) {
numKey = textStr.length + '|' + styles.fontSize + '|' + styles.fontFamily;
bBox = renderer.cache[numKey];
}

// No cache found
if (!bBox) {

// SVG elements
if (element.namespaceURI === SVG_NS || renderer.forExport) {
try { // Fails in Firefox if the container has display: none.
Expand Down Expand Up @@ -2488,7 +2501,11 @@ SVGElement.prototype = {
}
}

// Cache it
wrapper.bBox = bBox;
if (numKey) {
renderer.cache[numKey] = bBox;
}
}
return bBox;
},
Expand Down Expand Up @@ -2770,6 +2787,7 @@ SVGRenderer.prototype = {
renderer.defs = this.createElement('defs').add();
renderer.forExport = forExport;
renderer.gradients = {}; // Object where gradient SvgElements are stored
renderer.cache = {}; // Cache for numerical bounding boxes

renderer.setSize(width, height, false);

Expand Down Expand Up @@ -2973,7 +2991,9 @@ SVGRenderer.prototype = {
bBox;

while (words.length || rest.length) {
delete wrapper.bBox; // delete cache
if (words.length > 1) {
delete wrapper.bBox; // delete cache
}
bBox = wrapper.getBBox();
actualWidth = bBox.width;

Expand Down Expand Up @@ -4725,11 +4745,6 @@ Highcharts.VMLElement = VMLElement = {

skipAttr = true;

// text for rotated and non-rotated elements
} else if (key === 'text') {
this.bBox = null;
element.innerHTML = value;
skipAttr = true;
}


Expand Down Expand Up @@ -4945,6 +4960,7 @@ var VMLRendererExtension = { // inherit SVGRenderer
renderer.isVML = true;
renderer.box = box;
renderer.boxWrapper = boxWrapper;
renderer.cache = {};


renderer.setSize(width, height, false);
Expand Down Expand Up @@ -5979,7 +5995,7 @@ Tick.prototype = {
}

// Don't draw ticks so close they appear as a gray mass
} else if (mark) {
} else if (mark) {
tick.mark = mark.destroy();
}
}
Expand Down
32 changes: 24 additions & 8 deletions js/highstock.src.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ var UNDEFINED,
NONE = 'none',
M = 'M',
L = 'L',
numRegex = /^[0-9]+$/,
/*
* Empirical lowest possible opacities for TRACKER_FILL
* IE6: 0.002
Expand Down Expand Up @@ -2438,9 +2439,21 @@ SVGElement.prototype = {
rotation = wrapper.rotation,
element = wrapper.element,
styles = wrapper.styles,
rad = rotation * deg2rad;
rad = rotation * deg2rad,
textStr = wrapper.textStr,
numKey;

// Since numbers are monospaced, and numerical labels appear a lot in a chart,
// we assume that a label of n characters has the same bounding box as others
// of the same length.
if (textStr === '' || numRegex.test(textStr)) {
numKey = textStr.length + '|' + styles.fontSize + '|' + styles.fontFamily;
bBox = renderer.cache[numKey];
}

// No cache found
if (!bBox) {

// SVG elements
if (element.namespaceURI === SVG_NS || renderer.forExport) {
try { // Fails in Firefox if the container has display: none.
Expand Down Expand Up @@ -2488,7 +2501,11 @@ SVGElement.prototype = {
}
}

// Cache it
wrapper.bBox = bBox;
if (numKey) {
renderer.cache[numKey] = bBox;
}
}
return bBox;
},
Expand Down Expand Up @@ -2770,6 +2787,7 @@ SVGRenderer.prototype = {
renderer.defs = this.createElement('defs').add();
renderer.forExport = forExport;
renderer.gradients = {}; // Object where gradient SvgElements are stored
renderer.cache = {}; // Cache for numerical bounding boxes

renderer.setSize(width, height, false);

Expand Down Expand Up @@ -2973,7 +2991,9 @@ SVGRenderer.prototype = {
bBox;

while (words.length || rest.length) {
delete wrapper.bBox; // delete cache
if (words.length > 1) {
delete wrapper.bBox; // delete cache
}
bBox = wrapper.getBBox();
actualWidth = bBox.width;

Expand Down Expand Up @@ -4725,11 +4745,6 @@ Highcharts.VMLElement = VMLElement = {

skipAttr = true;

// text for rotated and non-rotated elements
} else if (key === 'text') {
this.bBox = null;
element.innerHTML = value;
skipAttr = true;
}


Expand Down Expand Up @@ -4945,6 +4960,7 @@ var VMLRendererExtension = { // inherit SVGRenderer
renderer.isVML = true;
renderer.box = box;
renderer.boxWrapper = boxWrapper;
renderer.cache = {};


renderer.setSize(width, height, false);
Expand Down Expand Up @@ -5979,7 +5995,7 @@ Tick.prototype = {
}

// Don't draw ticks so close they appear as a gray mass
} else if (mark) {
} else if (mark) {
tick.mark = mark.destroy();
}
}
Expand Down
1 change: 1 addition & 0 deletions js/parts/Globals.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ var UNDEFINED,
NONE = 'none',
M = 'M',
L = 'L',
numRegex = /^[0-9]+$/,
/*
* Empirical lowest possible opacities for TRACKER_FILL
* IE6: 0.002
Expand Down
23 changes: 21 additions & 2 deletions js/parts/SvgRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -623,9 +623,21 @@ SVGElement.prototype = {
rotation = wrapper.rotation,
element = wrapper.element,
styles = wrapper.styles,
rad = rotation * deg2rad;
rad = rotation * deg2rad,
textStr = wrapper.textStr,
numKey;

// Since numbers are monospaced, and numerical labels appear a lot in a chart,
// we assume that a label of n characters has the same bounding box as others
// of the same length.
if (textStr === '' || numRegex.test(textStr)) {
numKey = textStr.length + '|' + styles.fontSize + '|' + styles.fontFamily;
bBox = renderer.cache[numKey];
}

// No cache found
if (!bBox) {

// SVG elements
if (element.namespaceURI === SVG_NS || renderer.forExport) {
try { // Fails in Firefox if the container has display: none.
Expand Down Expand Up @@ -673,7 +685,11 @@ SVGElement.prototype = {
}
}

// Cache it
wrapper.bBox = bBox;
if (numKey) {
renderer.cache[numKey] = bBox;
}
}
return bBox;
},
Expand Down Expand Up @@ -955,6 +971,7 @@ SVGRenderer.prototype = {
renderer.defs = this.createElement('defs').add();
renderer.forExport = forExport;
renderer.gradients = {}; // Object where gradient SvgElements are stored
renderer.cache = {}; // Cache for numerical bounding boxes

renderer.setSize(width, height, false);

Expand Down Expand Up @@ -1158,7 +1175,9 @@ SVGRenderer.prototype = {
bBox;

while (words.length || rest.length) {
delete wrapper.bBox; // delete cache
if (words.length > 1) {
delete wrapper.bBox; // delete cache
}
bBox = wrapper.getBBox();
actualWidth = bBox.width;

Expand Down
2 changes: 1 addition & 1 deletion js/parts/Tick.js
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ Tick.prototype = {
}

// Don't draw ticks so close they appear as a gray mass
} else if (mark) {
} else if (mark) {
tick.mark = mark.destroy();
}
}
Expand Down
6 changes: 1 addition & 5 deletions js/parts/VmlRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -399,11 +399,6 @@ Highcharts.VMLElement = VMLElement = {

skipAttr = true;

// text for rotated and non-rotated elements
} else if (key === 'text') {
this.bBox = null;
element.innerHTML = value;
skipAttr = true;
}


Expand Down Expand Up @@ -619,6 +614,7 @@ var VMLRendererExtension = { // inherit SVGRenderer
renderer.isVML = true;
renderer.box = box;
renderer.boxWrapper = boxWrapper;
renderer.cache = {};


renderer.setSize(width, height, false);
Expand Down
11 changes: 11 additions & 0 deletions samples/maps/coloraxis/dataclasses-labelformatter/demo.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#container {
height: 500px;
min-width: 310px;
max-width: 800px;
margin: 0 auto;
}
.loading {
margin-top: 10em;
text-align: center;
color: gray;
}
12 changes: 12 additions & 0 deletions samples/maps/coloraxis/dataclasses-labelformatter/demo.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://code.highcharts.com/modules/data.src.js"></script>
<script src="http://code.highcharts.com/modules/map.src.js"></script>
<script src="http://www.highcharts.local/samples/data/maps/world.js"></script>


<div id="container">
<div class="loading">
<i class="icon-spinner icon-spin icon-large"></i>
Loading data from Google Spreadsheets...
</div>
</div>
95 changes: 95 additions & 0 deletions samples/maps/coloraxis/dataclasses-labelformatter/demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
$(function () {




// Load the data from a Google Spreadsheet
// https://docs.google.com/a/highsoft.com/spreadsheet/pub?hl=en_GB&hl=en_GB&key=0AoIaUO7wH1HwdFJHaFI4eUJDYlVna3k5TlpuXzZubHc&output=html
Highcharts.data({


googleSpreadsheetKey: '0AoIaUO7wH1HwdFJHaFI4eUJDYlVna3k5TlpuXzZubHc',

// custom handler when the spreadsheet is parsed
parsed: function (columns) {

// Read the columns into the data array
var data = [];
$.each(columns[0], function (i, code) {
data.push({
code: code.toUpperCase(),
value: parseFloat(columns[2][i]),
name: columns[1][i]
})
});


// Initiate the chart
$('#container').highcharts('Map', {
chart : {
borderWidth : 1
},

colors: ['rgba(19,64,117,0.1)', 'rgba(19,64,117,0.5)', 'rgba(19,64,117,1)'],

title : {
text : 'Data classes with legend label formatter'
},

mapNavigation: {
enabled: true
},

legend: {
title: {
text: 'Population density'
},
align: 'left',
verticalAlign: 'bottom',
floating: true,
labelFormatter: function () {
if (this.from === undefined) {
return 'Below ' + this.to;
} else if (this.to === undefined) {
return 'Above ' + this.from;
} else {
return this.from + ' to ' + this.to;
}

},
layout: 'vertical',
valueDecimals: 0,
backgroundColor: 'rgba(255,255,255,0.9)',
symbolRadius: 0,
symbolHeight: 14
},

colorAxis: {
dataClasses: [{
to: 20
}, {
from: 20,
to: 200
}, {
from: 200
}]
},

series : [{
data : data,
mapData: Highcharts.maps.world,
joinBy: 'code',
name: 'Population density',
states: {
hover: {
color: '#BADA55'
}
},
tooltip: {
valueSuffix: '/km²'
},
}]
});
}
});
});
Loading

0 comments on commit 1e97bff

Please sign in to comment.