Skip to content
This repository has been archived by the owner on Jul 29, 2019. It is now read-only.

Commit

Permalink
Graph3d: move Filter into DataGroup (#3159)
Browse files Browse the repository at this point in the history
* Graph3D: move Filter into DataGroup

The `Filter` instance within `Graph3d` is intimately connected to the graph data,
contained in a `DataGroup` instance. As such, it needs to be placed within `DataGroup`.

A consequence of this is that, in the final case of multiple graphs, each graph can be
animation separately. I regard this as an advantage, even though it will means more
initialization for the graphs (you have to initialize each separately for an animation.

An effort has been made to decouple `Graph3d` and `Filter` as much as possible. There
are still some relationships present, but it's more bother than it's worth to dissolve these.

In addition to moving the `Filter` instance, the following has been done:

- Added variable `style` to `DataGroup`
- Moved certain data-specific methods from `Graph3d` to `DataGroup`
- cleaned up some code and commenting

These changes have been tested with the following examples:

- `graph3d/10_styling`
- `graph3d/03_filter_data`
- `graph3d/04_animation`

* Add changes to filter
  • Loading branch information
wimrijnders authored and yotamberk committed Jul 21, 2017
1 parent 6aba9b2 commit b9aec48
Show file tree
Hide file tree
Showing 3 changed files with 266 additions and 129 deletions.
196 changes: 196 additions & 0 deletions lib/graph3d/DataGroup.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
var DataSet = require('../DataSet');
var DataView = require('../DataView');
var Range = require('./Range');
var Filter = require('./Filter');
var Settings = require('./Settings');
var Point3d = require('./Point3d');


/**
Expand Down Expand Up @@ -51,6 +54,8 @@ DataGroup.prototype.initializeData = function(graph3d, rawData, style) {

if (data.length == 0) return;

this.style = style;

// unsubscribe from the dataTable
if (this.dataSet) {
this.dataSet.off('*', this._onChange);
Expand Down Expand Up @@ -102,6 +107,27 @@ DataGroup.prototype.initializeData = function(graph3d, rawData, style) {
this._setRangeDefaults(valueRange, graph3d.defaultValueMin, graph3d.defaultValueMax);
this.valueRange = valueRange;
}

// Initialize data filter if a filter column is provided
var table = this.getDataTable();
if (table[0].hasOwnProperty('filter')) {
if (this.dataFilter === undefined) {
this.dataFilter = new Filter(this, 'filter', graph3d);
this.dataFilter.setOnLoadCallback(function() { graph3d.redraw(); });
}
}


var dataPoints;
if (this.dataFilter) {
// apply filtering
dataPoints = this.dataFilter._getDataPoints();
}
else {
// no filtering. load all data
dataPoints = this._getDataPoints(this.getDataTable());
}
return dataPoints;
};


Expand Down Expand Up @@ -290,4 +316,174 @@ DataGroup.prototype.getDataSet = function() {
};


/**
* Return all data values as a list of Point3d objects
*/
DataGroup.prototype.getDataPoints = function(data) {
var dataPoints = [];

for (var i = 0; i < data.length; i++) {
var point = new Point3d();
point.x = data[i][this.colX] || 0;
point.y = data[i][this.colY] || 0;
point.z = data[i][this.colZ] || 0;
point.data = data[i];

if (this.colValue !== undefined) {
point.value = data[i][this.colValue] || 0;
}

var obj = {};
obj.point = point;
obj.bottom = new Point3d(point.x, point.y, this.zRange.min);
obj.trans = undefined;
obj.screen = undefined;

dataPoints.push(obj);
}

return dataPoints;
};


/**
* Copy all values from the data table to a matrix.
*
* The provided values are supposed to form a grid of (x,y) positions.
* @private
*/
DataGroup.prototype.initDataAsMatrix = function(data) {
// TODO: store the created matrix dataPoints in the filters instead of
// reloading each time.
var x, y, i, obj;

// create two lists with all present x and y values
var dataX = this.getDistinctValues(this.colX, data);
var dataY = this.getDistinctValues(this.colY, data);

var dataPoints = this.getDataPoints(data);

// create a grid, a 2d matrix, with all values.
var dataMatrix = []; // temporary data matrix
for (i = 0; i < dataPoints.length; i++) {
obj = dataPoints[i];

// TODO: implement Array().indexOf() for Internet Explorer
var xIndex = dataX.indexOf(obj.point.x);
var yIndex = dataY.indexOf(obj.point.y);

if (dataMatrix[xIndex] === undefined) {
dataMatrix[xIndex] = [];
}

dataMatrix[xIndex][yIndex] = obj;
}

// fill in the pointers to the neighbors.
for (x = 0; x < dataMatrix.length; x++) {
for (y = 0; y < dataMatrix[x].length; y++) {
if (dataMatrix[x][y]) {
dataMatrix[x][y].pointRight = (x < dataMatrix.length-1) ? dataMatrix[x+1][y] : undefined;
dataMatrix[x][y].pointTop = (y < dataMatrix[x].length-1) ? dataMatrix[x][y+1] : undefined;
dataMatrix[x][y].pointCross =
(x < dataMatrix.length-1 && y < dataMatrix[x].length-1) ?
dataMatrix[x+1][y+1] :
undefined;
}
}
}

return dataPoints;
}


/**
* Return common information, if present
*/
DataGroup.prototype.getInfo = function() {
var dataFilter = this.dataFilter;
if (!dataFilter) return undefined;

return dataFilter.getLabel() + ': ' + dataFilter.getSelectedValue();
};


/**
* Reload the data
*/
DataGroup.prototype.reload = function() {
if (this.dataTable) {
this.setData(this.dataTable);
}
};


/**
* Filter the data based on the current filter
*
* @param {Array} data
* @returns {Array} dataPoints Array with point objects which can be drawn on
* screen
*/
DataGroup.prototype._getDataPoints = function (data) {
var dataPoints = [];

if (this.style === Settings.STYLE.GRID || this.style === Settings.STYLE.SURFACE) {
dataPoints = this.initDataAsMatrix(data);
}
else { // 'dot', 'dot-line', etc.
this._checkValueField(data);
dataPoints = this.getDataPoints(data);

if (this.style === Settings.STYLE.LINE) {
// Add next member points for line drawing
for (var i = 0; i < dataPoints.length; i++) {
if (i > 0) {
dataPoints[i - 1].pointNext = dataPoints[i];
}
}
}
}

return dataPoints;
};


/**
* Check if the state is consistent for the use of the value field.
*
* Throws if a problem is detected.
* @private
*/
DataGroup.prototype._checkValueField = function (data) {

var hasValueField = this.style === Settings.STYLE.BARCOLOR
|| this.style === Settings.STYLE.BARSIZE
|| this.style === Settings.STYLE.DOTCOLOR
|| this.style === Settings.STYLE.DOTSIZE;

if (!hasValueField) {
return; // No need to check further
}


// Following field must be present for the current graph style
if (this.colValue === undefined) {
throw new Error('Expected data to have '
+ ' field \'style\' '
+ ' for graph style \'' + this.style + '\''
);
}

// The data must also contain this field.
// Note that only first data element is checked.
if (data[0][this.colValue] === undefined) {
throw new Error('Expected data to have '
+ ' field \'' + this.colValue + '\' '
+ ' for graph style \'' + this.style + '\''
);
}
};


module.exports = DataGroup;
6 changes: 3 additions & 3 deletions lib/graph3d/Filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ var DataView = require('../DataView');
* @param {Graph} graph The graph
*/
function Filter (dataGroup, column, graph) {
this.data = dataGroup.getDataSet();
this.dataGroup = dataGroup;
this.column = column;
this.graph = graph; // the parent graph

Expand Down Expand Up @@ -133,8 +133,8 @@ Filter.prototype._getDataPoints = function(index) {
f.column = this.column;
f.value = this.values[index];

var dataView = new DataView(this.data,{filter: function (item) {return (item[f.column] == f.value);}}).get();
dataPoints = this.graph._getDataPoints(dataView);
var dataView = new DataView(this.dataGroup.getDataSet(), {filter: function (item) {return (item[f.column] == f.value);}}).get();
dataPoints = this.dataGroup._getDataPoints(dataView);

this.dataPoints[index] = dataPoints;
}
Expand Down
Loading

0 comments on commit b9aec48

Please sign in to comment.