From 9a028a8634a2b7e2e0a835de9b44166f533a1015 Mon Sep 17 00:00:00 2001 From: Wim Rijnders Date: Wed, 19 Oct 2016 12:50:48 +0200 Subject: [PATCH] Added step to defaults; fixes to detect and survive inconstent data --- lib/graph3d/Graph3d.js | 135 +++++++++++++++++++++++++++----------- lib/graph3d/StepNumber.js | 21 ++++++ 2 files changed, 119 insertions(+), 37 deletions(-) diff --git a/lib/graph3d/Graph3d.js b/lib/graph3d/Graph3d.js index 08ccd6bb..273d6cf8 100644 --- a/lib/graph3d/Graph3d.js +++ b/lib/graph3d/Graph3d.js @@ -12,6 +12,21 @@ var StepNumber = require('./StepNumber'); // Definitions private to module // ----------------------------------------------------------------------------- +/// enumerate the available styles +var 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,28 +91,28 @@ 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%', - // Following not in defaults (yet) but present in user settings - // These will be initialized as 'undefined' - //'showAnimationControls', - //'animationAutoStart' - // Following not in OPTIONKEYS because they require special handling, + style : STYLE.DOT, // Can't use Graph3d.STYLE here, not defined yet backgroundColor : undefined, dataColor : { fill : '#7DC1FF', stroke : '#3267D2', - strokeWidth: 1 // px + strokeWidth: 1 // px }, cameraPosition : { @@ -154,6 +169,15 @@ function safeCopy(src, dst, fields) { // Class Graph3d // ----------------------------------------------------------------------------- +/** + * Enumerate the available styles. + * + * This definition retained for external compatibility + * (It should be internal, but you never know) + */ +Graph3d.STYLE = STYLE; + + /** * @constructor Graph3d * Graph3d displays data in 3d. @@ -201,8 +225,6 @@ function Graph3d(container, data, options) { this.showLegend = undefined; // auto by default (based on graph style) - this.style = Graph3d.STYLE.DOT; - this.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window? // the column indexes @@ -403,10 +425,10 @@ Graph3d.prototype._setSpecialSettings = function(src, dst) { } this._setDataColor(src.dataColor, dst); + this._setStyle(src.style, dst); this._setCameraPosition(src.cameraPosition, dst); /* TODO - setStyle(src.style, dst); if (src.tooltip !== undefined) { dst.showTooltip = src.tooltip; @@ -416,6 +438,39 @@ End TODO */ } +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 STYLE) { + if (STYLE[n] === style) { + valid = true; + break; + } + } + + if (!valid) { + throw new Error('Style \'' + style + '\' is invalid'); + } + + styleNumber = style; + } + + dst.style = styleNumber; +} + /** @@ -521,19 +576,7 @@ Graph3d.prototype.setCameraPosition = function(pos) { // ----------------------------------------------------------------------------- -/// 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 -}; + /** * Retrieve the style index from given styleName @@ -686,13 +729,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();}); @@ -750,7 +791,9 @@ 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; @@ -850,6 +893,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(); @@ -1077,12 +1144,6 @@ Graph3d.prototype.setOptions = function (options) { // 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; if (options.xBarWidth !== undefined) this.defaultXBarWidth = options.xBarWidth; diff --git a/lib/graph3d/StepNumber.js b/lib/graph3d/StepNumber.js index 72a73839..9675b4f8 100644 --- a/lib/graph3d/StepNumber.js +++ b/lib/graph3d/StepNumber.js @@ -35,6 +35,17 @@ function StepNumber(start, end, step, prettyStep) { this.setRange(start, end, step, prettyStep); }; + +/** + * Check for input values, to prevent disasters from happening + * + * Source: http://stackoverflow.com/a/1830844 + */ +StepNumber.prototype.isNumeric = function(n) { + return !isNaN(parseFloat(n)) && isFinite(n); +}; + + /** * Set a new range: start, end and step. * @@ -45,6 +56,16 @@ function StepNumber(start, end, step, prettyStep) { * To a pretty step size (like 1, 2, 5, 10, 20, 50, ...) */ StepNumber.prototype.setRange = function(start, end, step, prettyStep) { + if (!this.isNumeric(start)) { + throw new Error('Parameter \'start\' is not numeric; value: ' + start); + } + if (!this.isNumeric(end)) { + throw new Error('Parameter \'end\' is not numeric; value: ' + start); + } + if (!this.isNumeric(step)) { + throw new Error('Parameter \'step\' is not numeric; value: ' + start); + } + this._start = start ? start : 0; this._end = end ? end : 0;