|
|
@ -12,6 +12,21 @@ var StepNumber = require('./StepNumber'); |
|
|
|
// Definitions private to module
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
/// enumerate the available styles
|
|
|
|
Graph3d.STYLE = { |
|
|
|
BAR : 0, |
|
|
|
BARCOLOR: 1, |
|
|
|
BARSIZE : 2, |
|
|
|
DOT : 3, |
|
|
|
DOTLINE : 4, |
|
|
|
DOTCOLOR: 5, |
|
|
|
DOTSIZE : 6, |
|
|
|
GRID : 7, |
|
|
|
LINE : 8, |
|
|
|
SURFACE : 9 |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Field names in the options hash which are of relevance to the user. |
|
|
|
* |
|
|
@ -32,9 +47,9 @@ var OPTIONKEYS = [ |
|
|
|
'showGrid', |
|
|
|
'showPerspective', |
|
|
|
'showShadow', |
|
|
|
'showAnimationControls', |
|
|
|
'keepAspectRatio', |
|
|
|
'verticalRatio', |
|
|
|
'showAnimationControls', |
|
|
|
'animationInterval', |
|
|
|
'animationPreload', |
|
|
|
'animationAutoStart', |
|
|
@ -76,19 +91,37 @@ var DEFAULTS = { |
|
|
|
showPerspective : true, |
|
|
|
showShadow : false, |
|
|
|
keepAspectRatio : true, |
|
|
|
verticalRatio : 0.5, // 0.1 to 1.0, where 1.0 results in a 'cube'
|
|
|
|
animationInterval: 1000, // milliseconds
|
|
|
|
animationPreload : false, |
|
|
|
verticalRatio : 0.5, // 0.1 to 1.0, where 1.0 results in a 'cube'
|
|
|
|
|
|
|
|
showAnimationControls: undefined, // auto by default
|
|
|
|
animationInterval : 1000, // milliseconds
|
|
|
|
animationPreload : false, |
|
|
|
animationAutoStart : undefined, // auto by default
|
|
|
|
|
|
|
|
axisColor : '#4D4D4D', |
|
|
|
gridColor : '#D3D3D3', |
|
|
|
xCenter : '55%', |
|
|
|
yCenter : '50%' |
|
|
|
yCenter : '50%', |
|
|
|
|
|
|
|
|
|
|
|
// Following not in OPTIONKEYS because they require special handling,
|
|
|
|
|
|
|
|
style : Graph3d.STYLE.DOT, |
|
|
|
tooltip : false, |
|
|
|
showLegend : undefined, // auto by default (based on graph style)
|
|
|
|
backgroundColor : undefined, |
|
|
|
|
|
|
|
// Following not in defaults (yet) but present in user settings
|
|
|
|
// These will be initialized as 'undefined'
|
|
|
|
//'showAnimationControls',
|
|
|
|
//'animationAutoStart'
|
|
|
|
dataColor : { |
|
|
|
fill : '#7DC1FF', |
|
|
|
stroke : '#3267D2', |
|
|
|
strokeWidth: 1 // px
|
|
|
|
}, |
|
|
|
|
|
|
|
cameraPosition : { |
|
|
|
horizontal: 1.0, |
|
|
|
vertical : 0.5, |
|
|
|
distance : 1.7 |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -138,6 +171,7 @@ function safeCopy(src, dst, fields) { |
|
|
|
// Class Graph3d
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* @constructor Graph3d |
|
|
|
* Graph3d displays data in 3d. |
|
|
@ -160,6 +194,8 @@ function Graph3d(container, data, options) { |
|
|
|
this.dataTable = null; // The original data table
|
|
|
|
this.dataPoints = null; // The table with point objects
|
|
|
|
|
|
|
|
// create a frame and canvas
|
|
|
|
this.create(); |
|
|
|
|
|
|
|
//
|
|
|
|
// Start Settings
|
|
|
@ -169,24 +205,19 @@ function Graph3d(container, data, options) { |
|
|
|
forceCopy(DEFAULTS, this, OPTIONKEYS); |
|
|
|
|
|
|
|
// Following are internal fields, not part of the user settings
|
|
|
|
this.margin = 10; // px
|
|
|
|
this.showGrayBottom = false; // TODO: this does not work correctly
|
|
|
|
this.margin = 10; // px
|
|
|
|
this.showGrayBottom = false; // TODO: this does not work correctly
|
|
|
|
this.showTooltip = false; |
|
|
|
this.dotSizeRatio = 0.02; // size of the dots as a fraction of the graph width
|
|
|
|
this.dotSizeRatio = 0.02; // size of the dots as a fraction of the graph width
|
|
|
|
this.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window?
|
|
|
|
|
|
|
|
// Handle the more complex ('special') fields
|
|
|
|
this._setSpecialSettings(DEFAULTS, this); |
|
|
|
|
|
|
|
// The rest of the fields.
|
|
|
|
// These require special attention in some way
|
|
|
|
// TODO: handle these
|
|
|
|
|
|
|
|
this.showLegend = undefined; // auto by default (based on graph style)
|
|
|
|
|
|
|
|
this.style = Graph3d.STYLE.DOT; |
|
|
|
|
|
|
|
|
|
|
|
this.camera = new Camera(); |
|
|
|
this.camera.setArmRotation(1.0, 0.5); |
|
|
|
this.camera.setArmLength(1.7); |
|
|
|
this.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window?
|
|
|
|
|
|
|
|
// the column indexes
|
|
|
|
this.colX = undefined; |
|
|
@ -210,21 +241,10 @@ function Graph3d(container, data, options) { |
|
|
|
this.yBarWidth = 1; |
|
|
|
// TODO: customize axis range
|
|
|
|
|
|
|
|
// colors
|
|
|
|
this.dataColor = { |
|
|
|
fill: '#7DC1FF', |
|
|
|
stroke: '#3267D2', |
|
|
|
strokeWidth: 1 // px
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// End Settings
|
|
|
|
//
|
|
|
|
|
|
|
|
// create a frame and canvas
|
|
|
|
this.create(); |
|
|
|
|
|
|
|
// apply options (also when undefined)
|
|
|
|
this.setOptions(options); |
|
|
|
|
|
|
@ -381,11 +401,100 @@ Graph3d.prototype._calcTranslations = function(points, sort) { |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// Methods for handling settings
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Special handling for certain parameters |
|
|
|
* |
|
|
|
* 'Special' here means: setting requires more than a simple copy |
|
|
|
*/ |
|
|
|
Graph3d.prototype._setSpecialSettings = function(src, dst) { |
|
|
|
if (src.backgroundColor !== undefined) { |
|
|
|
this._setBackgroundColor(src.backgroundColor, dst); |
|
|
|
} |
|
|
|
|
|
|
|
this._setDataColor(src.dataColor, dst); |
|
|
|
this._setStyle(src.style, dst); |
|
|
|
this._setShowLegend(src.showLegend, dst); |
|
|
|
this._setCameraPosition(src.cameraPosition, dst); |
|
|
|
|
|
|
|
// As special fields go, this is an easy one; just a translation of the name.
|
|
|
|
// Can't use this.tooltip directly, because that field exists internally
|
|
|
|
if (src.tooltip !== undefined) { |
|
|
|
dst.showTooltip = src.tooltip; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Set the value of setting 'showLegend' |
|
|
|
* |
|
|
|
* This depends on the value of the style fields, so it must be called |
|
|
|
* after the style field has been initialized. |
|
|
|
*/ |
|
|
|
Graph3d.prototype._setShowLegend = function(showLegend, dst) { |
|
|
|
if (showLegend === undefined) { |
|
|
|
// If the default was auto, make a choice for this field
|
|
|
|
var isAutoByDefault = (DEFAULTS.showLegend === undefined); |
|
|
|
|
|
|
|
if (isAutoByDefault) { |
|
|
|
// these styles default to having legends
|
|
|
|
var isLegendGraphStyle = this.style === Graph3d.STYLE.DOTCOLOR |
|
|
|
|| this.style === Graph3d.STYLE.DOTSIZE; |
|
|
|
|
|
|
|
this.showLegend = isLegendGraphStyle; |
|
|
|
} else { |
|
|
|
// Leave current value as is
|
|
|
|
} |
|
|
|
} else { |
|
|
|
dst.showLegend = showLegend; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Graph3d.prototype._setStyle = function(style, dst) { |
|
|
|
if (style === undefined) { |
|
|
|
return; // Nothing to do
|
|
|
|
} |
|
|
|
|
|
|
|
var styleNumber; |
|
|
|
|
|
|
|
if (typeof style === 'string') { |
|
|
|
styleNumber = this._getStyleNumber(style); |
|
|
|
|
|
|
|
if (styleNumber === -1 ) { |
|
|
|
throw new Error('Style \'' + style + '\' is invalid'); |
|
|
|
} |
|
|
|
} else { |
|
|
|
// Do a pedantic check on style number value
|
|
|
|
var valid = false; |
|
|
|
for (var n in Graph3d.STYLE) { |
|
|
|
if (Graph3d.STYLE[n] === style) { |
|
|
|
valid = true; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (!valid) { |
|
|
|
throw new Error('Style \'' + style + '\' is invalid'); |
|
|
|
} |
|
|
|
|
|
|
|
styleNumber = style; |
|
|
|
} |
|
|
|
|
|
|
|
dst.style = styleNumber; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Set the background styling for the graph |
|
|
|
* @param {string | {fill: string, stroke: string, strokeWidth: string}} backgroundColor |
|
|
|
*/ |
|
|
|
Graph3d.prototype._setBackgroundColor = function(backgroundColor) { |
|
|
|
Graph3d.prototype._setBackgroundColor = function(backgroundColor, dst) { |
|
|
|
var fill = 'white'; |
|
|
|
var stroke = 'gray'; |
|
|
|
var strokeWidth = 1; |
|
|
@ -400,34 +509,92 @@ Graph3d.prototype._setBackgroundColor = function(backgroundColor) { |
|
|
|
if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke; |
|
|
|
if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth; |
|
|
|
} |
|
|
|
else if (backgroundColor === undefined) { |
|
|
|
// use use defaults
|
|
|
|
} |
|
|
|
else { |
|
|
|
throw new Error('Unsupported type of backgroundColor'); |
|
|
|
} |
|
|
|
|
|
|
|
this.frame.style.backgroundColor = fill; |
|
|
|
this.frame.style.borderColor = stroke; |
|
|
|
this.frame.style.borderWidth = strokeWidth + 'px'; |
|
|
|
this.frame.style.borderStyle = 'solid'; |
|
|
|
dst.frame.style.backgroundColor = fill; |
|
|
|
dst.frame.style.borderColor = stroke; |
|
|
|
dst.frame.style.borderWidth = strokeWidth + 'px'; |
|
|
|
dst.frame.style.borderStyle = 'solid'; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/// enumerate the available styles
|
|
|
|
Graph3d.STYLE = { |
|
|
|
BAR: 0, |
|
|
|
BARCOLOR: 1, |
|
|
|
BARSIZE: 2, |
|
|
|
DOT : 3, |
|
|
|
DOTLINE : 4, |
|
|
|
DOTCOLOR: 5, |
|
|
|
DOTSIZE: 6, |
|
|
|
GRID : 7, |
|
|
|
LINE: 8, |
|
|
|
SURFACE : 9 |
|
|
|
Graph3d.prototype._setDataColor = function(dataColor, dst) { |
|
|
|
if (dataColor === undefined) { |
|
|
|
return; // Nothing to do
|
|
|
|
} |
|
|
|
|
|
|
|
if (dst.dataColor === undefined) { |
|
|
|
dst.dataColor = {}; |
|
|
|
} |
|
|
|
|
|
|
|
if (typeof dataColor === 'string') { |
|
|
|
dst.dataColor.fill = dataColor; |
|
|
|
dst.dataColor.stroke = dataColor; |
|
|
|
} |
|
|
|
else { |
|
|
|
if (dataColor.fill) { |
|
|
|
dst.dataColor.fill = dataColor.fill; |
|
|
|
} |
|
|
|
if (dataColor.stroke) { |
|
|
|
dst.dataColor.stroke = dataColor.stroke; |
|
|
|
} |
|
|
|
if (dataColor.strokeWidth !== undefined) { |
|
|
|
dst.dataColor.strokeWidth = dataColor.strokeWidth; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Graph3d.prototype._setCameraPosition = function(cameraPosition, dst) { |
|
|
|
var camPos = cameraPosition; |
|
|
|
if (camPos === undefined) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (dst.camera === undefined) { |
|
|
|
dst.camera = new Camera(); |
|
|
|
} |
|
|
|
|
|
|
|
dst.camera.setArmRotation(camPos.horizontal, camPos.vertical); |
|
|
|
dst.camera.setArmLength(camPos.distance); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Public methods for specific settings
|
|
|
|
//
|
|
|
|
|
|
|
|
/** |
|
|
|
* Set the rotation and distance of the camera |
|
|
|
* @param {Object} pos An object with the camera position. The object |
|
|
|
* contains three parameters: |
|
|
|
* - horizontal {Number} |
|
|
|
* The horizontal rotation, between 0 and 2*PI. |
|
|
|
* Optional, can be left undefined. |
|
|
|
* - vertical {Number} |
|
|
|
* The vertical rotation, between 0 and 0.5*PI |
|
|
|
* if vertical=0.5*PI, the graph is shown from the |
|
|
|
* top. Optional, can be left undefined. |
|
|
|
* - distance {Number} |
|
|
|
* The (normalized) distance of the camera to the |
|
|
|
* center of the graph, a value between 0.71 and 5.0. |
|
|
|
* Optional, can be left undefined. |
|
|
|
*/ |
|
|
|
Graph3d.prototype.setCameraPosition = function(pos) { |
|
|
|
this._setCameraPosition(pos, this); |
|
|
|
this.redraw(); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// End methods for handling settings
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* Retrieve the style index from given styleName |
|
|
|
* @param {string} styleName Style name such as 'dot', 'grid', 'dot-line' |
|
|
@ -579,13 +746,11 @@ Graph3d.prototype._dataInitialize = function (rawData, style) { |
|
|
|
this.colX = 'x'; |
|
|
|
this.colY = 'y'; |
|
|
|
this.colZ = 'z'; |
|
|
|
this.colValue = 'style'; |
|
|
|
this.colFilter = 'filter'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 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!
|
|
|
|
|
|
|
|
if (this.dataFilter === undefined) { |
|
|
|
this.dataFilter = new Filter(rawData, this.colFilter, this); |
|
|
|
this.dataFilter.setOnLoadCallback(function() {me.redraw();}); |
|
|
@ -643,17 +808,15 @@ 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; |
|
|
|
|
|
|
|
if (this.colValue !== undefined) { |
|
|
|
// 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); |
|
|
|
this.valueMin = (this.defaultValueMin !== undefined) ? this.defaultValueMin : valueRange.min; |
|
|
|
this.valueMax = (this.defaultValueMax !== undefined) ? this.defaultValueMax : valueRange.max; |
|
|
|
if (this.valueMax <= this.valueMin) this.valueMax = this.valueMin + 1; |
|
|
|
} |
|
|
|
|
|
|
|
// these styles default to having legends
|
|
|
|
var isLegendGraphStyle = this.style === Graph3d.STYLE.DOTCOLOR || this.style === Graph3d.STYLE.DOTSIZE; |
|
|
|
this.showLegend = (this.defaultShowLegend !== undefined) ? this.defaultShowLegend : isLegendGraphStyle; |
|
|
|
|
|
|
|
// set the scale dependent on the ranges.
|
|
|
|
this._setScale(); |
|
|
|
}; |
|
|
@ -743,6 +906,30 @@ 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 + '\'' |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// copy all values from the google data table to a list with Point3d objects
|
|
|
|
for (i = 0; i < data.length; i++) { |
|
|
|
point = new Point3d(); |
|
|
@ -901,37 +1088,6 @@ Graph3d.prototype._resizeCenter = function() { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* Set the rotation and distance of the camera |
|
|
|
* @param {Object} pos An object with the camera position. The object |
|
|
|
* contains three parameters: |
|
|
|
* - horizontal {Number} |
|
|
|
* The horizontal rotation, between 0 and 2*PI. |
|
|
|
* Optional, can be left undefined. |
|
|
|
* - vertical {Number} |
|
|
|
* The vertical rotation, between 0 and 0.5*PI |
|
|
|
* if vertical=0.5*PI, the graph is shown from the |
|
|
|
* top. Optional, can be left undefined. |
|
|
|
* - distance {Number} |
|
|
|
* The (normalized) distance of the camera to the |
|
|
|
* center of the graph, a value between 0.71 and 5.0. |
|
|
|
* Optional, can be left undefined. |
|
|
|
*/ |
|
|
|
Graph3d.prototype.setCameraPosition = function(pos) { |
|
|
|
if (pos === undefined) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (pos.horizontal !== undefined && pos.vertical !== undefined) { |
|
|
|
this.camera.setArmRotation(pos.horizontal, pos.vertical); |
|
|
|
} |
|
|
|
|
|
|
|
if (pos.distance !== undefined) { |
|
|
|
this.camera.setArmLength(pos.distance); |
|
|
|
} |
|
|
|
|
|
|
|
this.redraw(); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
@ -995,17 +1151,10 @@ Graph3d.prototype.setOptions = function (options) { |
|
|
|
// Handle the parameters which can be simply copied over
|
|
|
|
safeCopy(options, this, OPTIONKEYS); |
|
|
|
|
|
|
|
// Handle the rest of the parameters
|
|
|
|
if (options.showLegend !== undefined) this.defaultShowLegend = options.showLegend; |
|
|
|
|
|
|
|
if (options.style !== undefined) { |
|
|
|
var styleNumber = this._getStyleNumber(options.style); |
|
|
|
if (styleNumber !== -1) { |
|
|
|
this.style = styleNumber; |
|
|
|
} |
|
|
|
} |
|
|
|
if (options.tooltip !== undefined) this.showTooltip = options.tooltip; |
|
|
|
// Handle the more complex ('special') fields
|
|
|
|
this._setSpecialSettings(options, this); |
|
|
|
|
|
|
|
// Handle the rest of the parameters
|
|
|
|
if (options.xBarWidth !== undefined) this.defaultXBarWidth = options.xBarWidth; |
|
|
|
if (options.yBarWidth !== undefined) this.defaultYBarWidth = options.yBarWidth; |
|
|
|
if (options.xMin !== undefined) this.defaultXMin = options.xMin; |
|
|
@ -1019,34 +1168,6 @@ Graph3d.prototype.setOptions = function (options) { |
|
|
|
if (options.zMax !== undefined) this.defaultZMax = options.zMax; |
|
|
|
if (options.valueMin !== undefined) this.defaultValueMin = options.valueMin; |
|
|
|
if (options.valueMax !== undefined) this.defaultValueMax = options.valueMax; |
|
|
|
if (options.backgroundColor !== undefined) this._setBackgroundColor(options.backgroundColor); |
|
|
|
|
|
|
|
if (options.cameraPosition !== undefined) cameraPosition = options.cameraPosition; |
|
|
|
|
|
|
|
if (cameraPosition !== undefined) { |
|
|
|
this.camera.setArmRotation(cameraPosition.horizontal, cameraPosition.vertical); |
|
|
|
this.camera.setArmLength(cameraPosition.distance); |
|
|
|
} |
|
|
|
|
|
|
|
// colors
|
|
|
|
if (options.dataColor) { |
|
|
|
if (typeof options.dataColor === 'string') { |
|
|
|
this.dataColor.fill = options.dataColor; |
|
|
|
this.dataColor.stroke = options.dataColor; |
|
|
|
} |
|
|
|
else { |
|
|
|
if (options.dataColor.fill) { |
|
|
|
this.dataColor.fill = options.dataColor.fill; |
|
|
|
} |
|
|
|
if (options.dataColor.stroke) { |
|
|
|
this.dataColor.stroke = options.dataColor.stroke; |
|
|
|
} |
|
|
|
if (options.dataColor.strokeWidth !== undefined) { |
|
|
|
this.dataColor.strokeWidth = options.dataColor.strokeWidth; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
this.setSize(this.width, this.height); |
|
|
|