|
|
@ -202,21 +202,23 @@ Graph3d.prototype._convert3Dto2D = function(point3d) { |
|
|
|
* camera |
|
|
|
*/ |
|
|
|
Graph3d.prototype._convertPointToTranslation = function(point3d) { |
|
|
|
var ax = point3d.x * this.scale.x, |
|
|
|
var cameraLocation = this.camera.getCameraLocation(), |
|
|
|
cameraRotation = this.camera.getCameraRotation(), |
|
|
|
ax = point3d.x * this.scale.x, |
|
|
|
ay = point3d.y * this.scale.y, |
|
|
|
az = point3d.z * this.scale.z, |
|
|
|
|
|
|
|
cx = this.camera.getCameraLocation().x, |
|
|
|
cy = this.camera.getCameraLocation().y, |
|
|
|
cz = this.camera.getCameraLocation().z, |
|
|
|
cx = CameraLocation.x, |
|
|
|
cy = CameraLocation.y, |
|
|
|
cz = CameraLocation.z, |
|
|
|
|
|
|
|
// calculate angles
|
|
|
|
sinTx = Math.sin(this.camera.getCameraRotation().x), |
|
|
|
cosTx = Math.cos(this.camera.getCameraRotation().x), |
|
|
|
sinTy = Math.sin(this.camera.getCameraRotation().y), |
|
|
|
cosTy = Math.cos(this.camera.getCameraRotation().y), |
|
|
|
sinTz = Math.sin(this.camera.getCameraRotation().z), |
|
|
|
cosTz = Math.cos(this.camera.getCameraRotation().z), |
|
|
|
sinTx = Math.sin(CameraRotation.x), |
|
|
|
cosTx = Math.cos(CameraRotation.x), |
|
|
|
sinTy = Math.sin(CameraRotation.y), |
|
|
|
cosTy = Math.cos(CameraRotation.y), |
|
|
|
sinTz = Math.sin(CameraRotation.z), |
|
|
|
cosTz = Math.cos(CameraRotation.z), |
|
|
|
|
|
|
|
// calculate translation
|
|
|
|
dx = cosTy * (sinTz * (ay - cy) + cosTz * (ax - cx)) - sinTy * (az - cz), |
|
|
@ -291,47 +293,6 @@ Graph3d.prototype._calcTranslations = function(points, sort) { |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Determine the indexes of the data columns, based on the given style and data |
|
|
|
* @param {DataSet} data |
|
|
|
* @param {Number} style |
|
|
|
*/ |
|
|
|
Graph3d.prototype._determineColumnIndexes = function(data, style) { |
|
|
|
if (this.style === Graph3d.STYLE.DOT || |
|
|
|
this.style === Graph3d.STYLE.DOTLINE || |
|
|
|
this.style === Graph3d.STYLE.LINE || |
|
|
|
this.style === Graph3d.STYLE.GRID || |
|
|
|
this.style === Graph3d.STYLE.SURFACE || |
|
|
|
this.style === Graph3d.STYLE.BAR) { |
|
|
|
// 3 columns expected, and optionally a 4th with filter values
|
|
|
|
this.colX = 0; |
|
|
|
this.colY = 1; |
|
|
|
this.colZ = 2; |
|
|
|
this.colValue = undefined; |
|
|
|
|
|
|
|
if (data.getNumberOfColumns() > 3) { |
|
|
|
this.colFilter = 3; |
|
|
|
} |
|
|
|
} |
|
|
|
else if (this.style === Graph3d.STYLE.DOTCOLOR || |
|
|
|
this.style === Graph3d.STYLE.DOTSIZE || |
|
|
|
this.style === Graph3d.STYLE.BARCOLOR || |
|
|
|
this.style === Graph3d.STYLE.BARSIZE) { |
|
|
|
// 4 columns expected, and optionally a 5th with filter values
|
|
|
|
this.colX = 0; |
|
|
|
this.colY = 1; |
|
|
|
this.colZ = 2; |
|
|
|
this.colValue = 3; |
|
|
|
|
|
|
|
if (data.getNumberOfColumns() > 4) { |
|
|
|
this.colFilter = 4; |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
throw new Error('Unknown style "' + this.style + '"'); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
Graph3d.prototype.getNumberOfRows = function(data) { |
|
|
|
return data.length; |
|
|
|
} |
|
|
@ -360,14 +321,57 @@ Graph3d.prototype.getDistinctValues = function(data, column) { |
|
|
|
|
|
|
|
|
|
|
|
Graph3d.prototype.getColumnRange = function(data,column) { |
|
|
|
var minMax = {min:data[0][column],max:data[0][column]}; |
|
|
|
var minMax; |
|
|
|
|
|
|
|
for (var i = 0; i < data.length; i++) { |
|
|
|
if (minMax.min > data[i][column]) { minMax.min = data[i][column]; } |
|
|
|
if (minMax.max < data[i][column]) { minMax.max = data[i][column]; } |
|
|
|
var item = data[i][column]; |
|
|
|
|
|
|
|
if (i === 0) { |
|
|
|
minMax = { min: item, max: item}; |
|
|
|
} else { |
|
|
|
if (minMax.min > item) { minMax.min = item; } |
|
|
|
if (minMax.max < item) { minMax.max = item; } |
|
|
|
} |
|
|
|
} |
|
|
|
return minMax; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Check if the state is consistent for the use of the value field. |
|
|
|
* |
|
|
|
* Throws if a problem is detected. |
|
|
|
*/ |
|
|
|
Graph3d.prototype._checkValueField = function (data) { |
|
|
|
|
|
|
|
var hasValueField = this.style === Graph3d.STYLE.BARCOLOR |
|
|
|
|| this.style === Graph3d.STYLE.BARSIZE |
|
|
|
|| this.style === Graph3d.STYLE.DOTCOLOR |
|
|
|
|| this.style === Graph3d.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 + '\'' |
|
|
|
); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Initialize the data from the data table. Calculate minimum and maximum values |
|
|
|
* and column index values |
|
|
@ -409,12 +413,6 @@ Graph3d.prototype._dataInitialize = function (rawData, style) { |
|
|
|
}; |
|
|
|
this.dataSet.on('*', this._onChange); |
|
|
|
|
|
|
|
// _determineColumnIndexes
|
|
|
|
// getNumberOfRows (points)
|
|
|
|
// getNumberOfColumns (x,y,z,v,t,t1,t2...)
|
|
|
|
// getDistinctValues (unique values?)
|
|
|
|
// getColumnRange
|
|
|
|
|
|
|
|
// determine the location of x,y,z,value,filter columns
|
|
|
|
this.colX = 'x'; |
|
|
|
this.colY = 'y'; |
|
|
@ -422,7 +420,8 @@ Graph3d.prototype._dataInitialize = function (rawData, style) { |
|
|
|
|
|
|
|
// check if a filter column is provided
|
|
|
|
if (data[0].hasOwnProperty('filter')) { |
|
|
|
this.colFilter = 'filter'; // Bugfix: only set this field if it's actually present!
|
|
|
|
// Only set this field if it's actually present
|
|
|
|
this.colFilter = 'filter'; |
|
|
|
|
|
|
|
if (this.dataFilter === undefined) { |
|
|
|
this.dataFilter = new Filter(rawData, this.colFilter, this); |
|
|
@ -481,7 +480,6 @@ Graph3d.prototype._dataInitialize = function (rawData, style) { |
|
|
|
if (this.zMax <= this.zMin) this.zMax = this.zMin + 1; |
|
|
|
this.zStep = (this.defaultZStep !== undefined) ? this.defaultZStep : (this.zMax-this.zMin)/5; |
|
|
|
|
|
|
|
// Bugfix: Only handle field 'style' if it's actually present
|
|
|
|
if (data[0].hasOwnProperty('style')) { |
|
|
|
this.colValue = 'style'; |
|
|
|
var valueRange = this.getColumnRange(data,this.colValue); |
|
|
@ -579,29 +577,7 @@ Graph3d.prototype._getDataPoints = function (data) { |
|
|
|
} |
|
|
|
} |
|
|
|
else { // 'dot', 'dot-line', etc.
|
|
|
|
|
|
|
|
// Bugfix: ensure value field is present in data if expected
|
|
|
|
var hasValueField = this.style === Graph3d.STYLE.BARCOLOR |
|
|
|
|| this.style === Graph3d.STYLE.BARSIZE |
|
|
|
|| this.style === Graph3d.STYLE.DOTCOLOR |
|
|
|
|| this.style === Graph3d.STYLE.DOTSIZE; |
|
|
|
|
|
|
|
if (hasValueField) { |
|
|
|
if (this.colValue === undefined) { |
|
|
|
throw new Error('Expected data to have ' |
|
|
|
+ ' field \'style\' ' |
|
|
|
+ ' for graph style \'' + this.style + '\'' |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
if (data[0][this.colValue] === undefined) { |
|
|
|
throw new Error('Expected data to have ' |
|
|
|
+ ' field \'' + this.colValue + '\' ' |
|
|
|
+ ' for graph style \'' + this.style + '\'' |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
this._checkValueField(data); |
|
|
|
|
|
|
|
// copy all values from the google data table to a list with Point3d objects
|
|
|
|
for (i = 0; i < data.length; i++) { |
|
|
@ -902,6 +878,7 @@ Graph3d.prototype._redrawClear = function() { |
|
|
|
*/ |
|
|
|
Graph3d.prototype._getLegendWidth = function() { |
|
|
|
var width; |
|
|
|
|
|
|
|
if (this.style === Graph3d.STYLE.DOTSIZE) { |
|
|
|
var dotSize = this.frame.clientWidth * this.dotSizeRatio; |
|
|
|
width = dotSize / 2 + dotSize * 2; |
|
|
@ -920,12 +897,16 @@ Graph3d.prototype._getLegendWidth = function() { |
|
|
|
Graph3d.prototype._redrawLegend = function() { |
|
|
|
|
|
|
|
//Return without drawing anything, if no legend is specified
|
|
|
|
if (this.showLegend !== true) {return;} |
|
|
|
if (this.showLegend !== true) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// Do not draw legend when graph style does not support
|
|
|
|
if (this.style === Graph3d.STYLE.LINE |
|
|
|
|| this.style === Graph3d.STYLE.BARSIZE //TODO add legend support for BARSIZE
|
|
|
|
){return;} |
|
|
|
){ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// Legend types - size and color. Determine if size legend.
|
|
|
|
var isSizeLegend = (this.style === Graph3d.STYLE.BARSIZE |
|
|
@ -1000,11 +981,9 @@ Graph3d.prototype._redrawLegend = function() { |
|
|
|
var y; |
|
|
|
while (!step.end()) { |
|
|
|
y = bottom - (step.getCurrent() - legendMin) / (legendMax - legendMin) * height; |
|
|
|
|
|
|
|
ctx.beginPath(); |
|
|
|
ctx.moveTo(left - gridLineLen, y); |
|
|
|
ctx.lineTo(left, y); |
|
|
|
ctx.stroke(); |
|
|
|
from = new Point2d(left - gridLineLen, y); |
|
|
|
to = new Point2d(left, y); |
|
|
|
this._line(ctx, from, to); |
|
|
|
|
|
|
|
ctx.textAlign = 'right'; |
|
|
|
ctx.textBaseline = 'middle'; |
|
|
@ -1018,7 +997,6 @@ Graph3d.prototype._redrawLegend = function() { |
|
|
|
ctx.textBaseline = 'top'; |
|
|
|
var label = this.legendLabel; |
|
|
|
ctx.fillText(label, right, bottom + this.margin); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
@ -1406,9 +1384,9 @@ Graph3d.prototype._redrawDataGrid = function() { |
|
|
|
if (this.style === Graph3d.STYLE.SURFACE) { |
|
|
|
for (i = 0; i < this.dataPoints.length; i++) { |
|
|
|
point = this.dataPoints[i]; |
|
|
|
right = this.dataPoints[i].pointRight; |
|
|
|
top = this.dataPoints[i].pointTop; |
|
|
|
cross = this.dataPoints[i].pointCross; |
|
|
|
right = point.pointRight; |
|
|
|
top = point.pointTop; |
|
|
|
cross = point.pointCross; |
|
|
|
|
|
|
|
if (point !== undefined && right !== undefined && top !== undefined && cross !== undefined) { |
|
|
|
|
|
|
@ -1466,30 +1444,25 @@ Graph3d.prototype._redrawDataGrid = function() { |
|
|
|
} |
|
|
|
} |
|
|
|
else { // grid style
|
|
|
|
for (i = 0; i < this.dataPoints.length; i++) { |
|
|
|
point = this.dataPoints[i]; |
|
|
|
right = this.dataPoints[i].pointRight; |
|
|
|
top = this.dataPoints[i].pointTop; |
|
|
|
|
|
|
|
if (point !== undefined && right !== undefined) { |
|
|
|
// calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0
|
|
|
|
zAvg = (point.point.z + right.point.z) / 2; |
|
|
|
h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; |
|
|
|
|
|
|
|
ctx.lineWidth = this._getStrokeWidth(point) * 2; |
|
|
|
ctx.strokeStyle = this._hsv2rgb(h, 1, 1); |
|
|
|
this._line(ctx, point.screen, right.screen); |
|
|
|
var drawGridLine = function(ctx, from, to) { |
|
|
|
if (from === undefined || to === undefined) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (point !== undefined && top !== undefined) { |
|
|
|
// calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0
|
|
|
|
zAvg = (point.point.z + top.point.z) / 2; |
|
|
|
h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; |
|
|
|
// calculate Hue from the current value. At zMin the hue is 240, at zMax the hue is 0
|
|
|
|
zAvg = (from.point.z + to.point.z) / 2; |
|
|
|
h = (1 - (zAvg - this.zMin) * this.scale.z / this.verticalRatio) * 240; |
|
|
|
|
|
|
|
ctx.lineWidth = this._getStrokeWidth(point) * 2; |
|
|
|
ctx.strokeStyle = this._hsv2rgb(h, 1, 1); |
|
|
|
this._line(ctx, point.screen, top.screen); |
|
|
|
} |
|
|
|
ctx.lineWidth = this._getStrokeWidth(from) * 2; |
|
|
|
ctx.strokeStyle = this._hsv2rgb(h, 1, 1); |
|
|
|
this._line(ctx, from.screen, to.screen); |
|
|
|
}; |
|
|
|
|
|
|
|
for (i = 0; i < this.dataPoints.length; i++) { |
|
|
|
point = this.dataPoints[i]; |
|
|
|
drawGridLine(ctx, point, point.pointRight); |
|
|
|
drawGridLine(ctx, point, point.pointTop); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|