Browse Source

Graph2d performance enhancement (#2281)

* Important performance enhancement supporting incremental changes to large graphs, these were too slow due to doing a full item conversion on each add().
Note: the data handling side of graph2d is currently somewhat over-engineered (=too complex) It can use some simplification....

* Fixed style based on feedback.
codeClimate
Ludo Stellingwerff 8 years ago
committed by Alexander Wunschik
parent
commit
876e0ab83e
2 changed files with 43 additions and 3 deletions
  1. +5
    -0
      lib/DataSet.js
  2. +38
    -3
      lib/timeline/component/LineGraph.js

+ 5
- 0
lib/DataSet.js View File

@ -910,6 +910,11 @@ DataSet.prototype._getItem = function (id, types) {
converted[field] = value; converted[field] = value;
} }
} }
if (!converted[this._fieldId]) {
converted[this._fieldId] = id;
}
return converted; return converted;
}; };

+ 38
- 3
lib/timeline/component/LineGraph.js View File

@ -332,7 +332,7 @@ LineGraph.prototype.setGroups = function (groups) {
}; };
LineGraph.prototype._onUpdate = function (ids) { LineGraph.prototype._onUpdate = function (ids) {
this._updateAllGroupData();
this._updateAllGroupData(ids);
}; };
LineGraph.prototype._onAdd = function (ids) { LineGraph.prototype._onAdd = function (ids) {
this._onUpdate(ids); this._onUpdate(ids);
@ -341,7 +341,7 @@ LineGraph.prototype._onRemove = function (ids) {
this._onUpdate(ids); this._onUpdate(ids);
}; };
LineGraph.prototype._onUpdateGroups = function (groupIds) { LineGraph.prototype._onUpdateGroups = function (groupIds) {
this._updateAllGroupData();
this._updateAllGroupData(null, groupIds);
}; };
LineGraph.prototype._onAddGroups = function (groupIds) { LineGraph.prototype._onAddGroups = function (groupIds) {
this._onUpdateGroups(groupIds); this._onUpdateGroups(groupIds);
@ -425,12 +425,22 @@ LineGraph.prototype._updateGroup = function (group, groupId) {
/** /**
* this updates all groups, it is used when there is an update the the itemset. * this updates all groups, it is used when there is an update the the itemset.
* *
* @param {Array} ids
* @param {Array} groupIds
* @private * @private
*/ */
LineGraph.prototype._updateAllGroupData = function () {
LineGraph.prototype._updateAllGroupData = function (ids, groupIds) {
if (this.itemsData != null) { if (this.itemsData != null) {
var groupsContent = {}; var groupsContent = {};
var items = this.itemsData.get(); var items = this.itemsData.get();
var fieldId = this.itemsData._fieldId;
var idMap = {};
if (ids){
ids.map(function (id) {
idMap[id] = id;
});
}
//pre-Determine array sizes, for more efficient memory claim //pre-Determine array sizes, for more efficient memory claim
var groupCounts = {}; var groupCounts = {};
for (var i = 0; i < items.length; i++) { for (var i = 0; i < items.length; i++) {
@ -441,6 +451,26 @@ LineGraph.prototype._updateAllGroupData = function () {
} }
groupCounts.hasOwnProperty(groupId) ? groupCounts[groupId]++ : groupCounts[groupId] = 1; groupCounts.hasOwnProperty(groupId) ? groupCounts[groupId]++ : groupCounts[groupId] = 1;
} }
//Pre-load arrays from existing groups if items are not changed (not in ids)
if (!groupIds && ids) {
for (var groupId in this.groups) {
if (this.groups.hasOwnProperty(groupId)) {
var group = this.groups[groupId];
var existing_items = group.getItems();
groupsContent[groupId] = existing_items.filter(function (item) {
return (item[fieldId] !== idMap[item[fieldId]]);
});
var newLength = groupCounts[groupId];
groupCounts[groupId] -= groupsContent[groupId].length;
if (groupsContent[groupId].length < newLength) {
groupsContent[groupId][newLength - 1] = {};
}
}
}
}
//Now insert data into the arrays. //Now insert data into the arrays.
for (var i = 0; i < items.length; i++) { for (var i = 0; i < items.length; i++) {
var item = items[i]; var item = items[i];
@ -448,6 +478,9 @@ LineGraph.prototype._updateAllGroupData = function () {
if (groupId === null || groupId === undefined) { if (groupId === null || groupId === undefined) {
groupId = UNGROUPED; groupId = UNGROUPED;
} }
if (!groupIds && ids && (item[fieldId] !== idMap[item[fieldId]])) {
continue;
}
if (!groupsContent.hasOwnProperty(groupId)) { if (!groupsContent.hasOwnProperty(groupId)) {
groupsContent[groupId] = new Array(groupCounts[groupId]); groupsContent[groupId] = new Array(groupCounts[groupId]);
} }
@ -456,6 +489,7 @@ LineGraph.prototype._updateAllGroupData = function () {
extended.x = util.convert(item.x, 'Date'); extended.x = util.convert(item.x, 'Date');
extended.orginalY = item.y; //real Y extended.orginalY = item.y; //real Y
extended.y = Number(item.y); extended.y = Number(item.y);
extended[fieldId] = item[fieldId];
var index= groupsContent[groupId].length - groupCounts[groupId]--; var index= groupsContent[groupId].length - groupCounts[groupId]--;
groupsContent[groupId][index] = extended; groupsContent[groupId][index] = extended;
@ -834,6 +868,7 @@ LineGraph.prototype._applySampling = function (groupIds, groupsData) {
// the global screen is used because changing the width of the yAxis may affect the increment, resulting in an endless loop // the global screen is used because changing the width of the yAxis may affect the increment, resulting in an endless loop
// of width changing of the yAxis. // of width changing of the yAxis.
//TODO: This assumes sorted data, but that's not guaranteed!
var xDistance = this.body.util.toGlobalScreen(dataContainer[dataContainer.length - 1].x) - this.body.util.toGlobalScreen(dataContainer[0].x); var xDistance = this.body.util.toGlobalScreen(dataContainer[dataContainer.length - 1].x) - this.body.util.toGlobalScreen(dataContainer[0].x);
var pointsPerPixel = amountOfPoints / xDistance; var pointsPerPixel = amountOfPoints / xDistance;
increment = Math.min(Math.ceil(0.2 * amountOfPoints), Math.max(1, Math.round(pointsPerPixel))); increment = Math.min(Math.ceil(0.2 * amountOfPoints), Math.max(1, Math.round(pointsPerPixel)));

Loading…
Cancel
Save