From 6fc876a0ed14388910fc72fb46ddcbd8205fda32 Mon Sep 17 00:00:00 2001 From: Wim Rijnders Date: Thu, 20 Oct 2016 19:51:04 +0200 Subject: [PATCH 1/3] Final cleanup for settings handling --- lib/graph3d/Graph3d.js | 903 +++++++++++++++++++++-------------------- 1 file changed, 452 insertions(+), 451 deletions(-) diff --git a/lib/graph3d/Graph3d.js b/lib/graph3d/Graph3d.js index 102312a9..ccbba1d1 100644 --- a/lib/graph3d/Graph3d.js +++ b/lib/graph3d/Graph3d.js @@ -8,9 +8,6 @@ var Filter = require('./Filter'); var Slider = require('./Slider'); var StepNumber = require('./StepNumber'); -// ----------------------------------------------------------------------------- -// Definitions private to module -// ----------------------------------------------------------------------------- /// enumerate the available styles Graph3d.STYLE = { @@ -28,59 +25,14 @@ Graph3d.STYLE = { /** - * Field names in the options hash which are of relevance to the user. - * - * Specifically, these are the fields which require no special handling, - * and can be directly copied over. - */ -var OPTIONKEYS = [ - 'width', - 'height', - 'filterLabel', - 'legendLabel', - 'xLabel', - 'yLabel', - 'zLabel', - 'xValueLabel', - 'yValueLabel', - 'zValueLabel', - 'showGrid', - 'showPerspective', - 'showShadow', - 'keepAspectRatio', - 'verticalRatio', - 'showAnimationControls', - 'animationInterval', - 'animationPreload', - 'animationAutoStart', - 'axisColor', - 'gridColor', - 'xCenter', - 'yCenter' -]; - - -/** - * Field names in the options hash which are of relevance to the user. - * - * Same as OPTIONKEYS, but internally these fields are stored with - * prefix 'default' in the name. + * Following label is used in the settings to describe values which + * should be determined by the code while running, from the current + * data and graph style. + * + * Using 'undefined' directly achieves the same thing, but this is + * more descriptive by describing the intent. */ -var PREFIXEDOPTIONKEYS = [ - 'xBarWidth', - 'yBarWidth', - 'valueMin', - 'valueMax', - 'xMin', - 'xMax', - 'xStep', - 'yMin', - 'yMax', - 'yStep', - 'zMin', - 'zMax', - 'zStep' -]; +var autoByDefault = undefined; /** @@ -89,15 +41,8 @@ var PREFIXEDOPTIONKEYS = [ * These are the values used when a Graph3d instance is initialized * without custom settings. * - * If a field is not in this list, a default value of 'undefined' can - * be assumed. Of course, it does no harm to set a field explicitly to - * 'undefined' here. - * - * A value of 'undefined' here normally means: - * - * 'derive from current data and graph style' - * - * In the code, this is indicated by the comment 'auto by default'. + * If a field is not in this list, a default value of 'autoByDefault' + * is assumed, which is just an alias for 'undefined'. */ var DEFAULTS = { width : '400px', @@ -114,29 +59,27 @@ var DEFAULTS = { showPerspective : true, showShadow : false, keepAspectRatio : true, - verticalRatio : 0.5, // 0.1 to 1.0, where 1.0 results in a 'cube' + verticalRatio : 0.5, // 0.1 to 1.0, where 1.0 results in a 'cube' - showAnimationControls: undefined, // auto by default - animationInterval : 1000, // milliseconds + showAnimationControls: autoByDefault, + animationInterval : 1000, // milliseconds animationPreload : false, - animationAutoStart : undefined, // auto by default + animationAutoStart : autoByDefault, axisColor : '#4D4D4D', gridColor : '#D3D3D3', xCenter : '55%', yCenter : '50%', - // Following require special handling, therefore not mentioned in the OPTIONKEYS tables. - style : Graph3d.STYLE.DOT, tooltip : false, - showLegend : undefined, // auto by default (based on graph style) - backgroundColor : undefined, + showLegend : autoByDefault, // determined by graph style + backgroundColor : autoByDefault, dataColor : { fill : '#7DC1FF', stroke : '#3267D2', - strokeWidth: 1 // px + strokeWidth: 1 // px }, cameraPosition : { @@ -145,102 +88,22 @@ var DEFAULTS = { distance : 1.7 }, - // Following stored internally with field prefix 'default' - // All these are 'auto by default' - - xBarWidth : undefined, - yBarWidth : undefined, - valueMin : undefined, - valueMax : undefined, - xMin : undefined, - xMax : undefined, - xStep : undefined, - yMin : undefined, - yMax : undefined, - yStep : undefined, - zMin : undefined, - zMax : undefined, - zStep : undefined + xBarWidth : autoByDefault, + yBarWidth : autoByDefault, + valueMin : autoByDefault, + valueMax : autoByDefault, + xMin : autoByDefault, + xMax : autoByDefault, + xStep : autoByDefault, + yMin : autoByDefault, + yMax : autoByDefault, + yStep : autoByDefault, + zMin : autoByDefault, + zMax : autoByDefault, + zStep : autoByDefault }; -/** - * Make first letter of parameter upper case. - * - * Source: http://stackoverflow.com/a/1026087 - */ -function capitalize(str) { - if (str === undefined || str === "") { - return str; - } - - return str.charAt(0).toUpperCase() + str.slice(1); -} - - -/** - * Add a prefix to a field name, taking style guide into account - */ -function prefixFieldName(prefix, fieldName) { - if (prefix === undefined || prefix === "") { - return fieldName; - } - - return prefix + capitalize(fieldName); -} - - -/** - * Forcibly copy fields from src to dst in a controlled manner. - * - * A given field in dst will always be overwitten. If this field - * is undefined or not present in src, the field in dst will - * be explicitly set to undefined. - * - * The intention here is to be able to reset all option fields. - * - * Only the fields mentioned in array 'fields' will be handled. - * - * @param fields array with names of fields to copy - * @param prefix optional; prefix to use for the target fields. - */ -function forceCopy(src, dst, fields, prefix) { - var srcKey; - var dstKey; - - for (var i in fields) { - srcKey = fields[i]; - dstKey = prefixFieldName(prefix, srcKey); - - dst[dstKey] = src[srcKey]; - } -} - - -/** - * Copy fields from src to dst in a safe and controlled manner. - * - * Only the fields mentioned in array 'fields' will be copied over, - * and only if these are actually defined. - * - * @param fields array with names of fields to copy - * @param prefix optional; prefix to use for the target fields. - */ -function safeCopy(src, dst, fields, prefix) { - var srcKey; - var dstKey; - - for (var i in fields) { - srcKey = fields[i]; - if (src[srcKey] === undefined) continue; - - dstKey = prefixFieldName(prefix, srcKey); - - dst[dstKey] = src[srcKey]; - } -} - - // ----------------------------------------------------------------------------- // Class Graph3d @@ -272,27 +135,7 @@ function Graph3d(container, data, options) { // create a frame and canvas this.create(); - // - // Set Defaults - // - - // Handle the defaults which can be simply copied over - forceCopy(DEFAULTS, this, OPTIONKEYS); - forceCopy(DEFAULTS, this, PREFIXEDOPTIONKEYS, 'default'); - - // Following are internal fields, not part of the user settings - 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.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); - - // - // End Set Defaults - // + this._setDefaults(); // the column indexes this.colX = undefined; @@ -459,278 +302,87 @@ 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 + * Retrieve the style index from given styleName + * @param {string} styleName Style name such as 'dot', 'grid', 'dot-line' + * @return {Number} styleNumber Enumeration value representing the style, or -1 + * when not found */ -Graph3d.prototype._setSpecialSettings = function(src, dst) { - if (src.backgroundColor !== undefined) { - this._setBackgroundColor(src.backgroundColor, dst); +Graph3d.prototype._getStyleNumber = function(styleName) { + switch (styleName) { + case 'dot': return Graph3d.STYLE.DOT; + case 'dot-line': return Graph3d.STYLE.DOTLINE; + case 'dot-color': return Graph3d.STYLE.DOTCOLOR; + case 'dot-size': return Graph3d.STYLE.DOTSIZE; + case 'line': return Graph3d.STYLE.LINE; + case 'grid': return Graph3d.STYLE.GRID; + case 'surface': return Graph3d.STYLE.SURFACE; + case 'bar': return Graph3d.STYLE.BAR; + case 'bar-color': return Graph3d.STYLE.BARCOLOR; + case 'bar-size': return Graph3d.STYLE.BARSIZE; } - 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; - } + return -1; }; - /** - * 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. + * Determine the indexes of the data columns, based on the given style and data + * @param {DataSet} data + * @param {Number} style */ -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); +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 (isAutoByDefault) { - // these styles default to having legends - var isLegendGraphStyle = this.style === Graph3d.STYLE.DOTCOLOR - || this.style === Graph3d.STYLE.DOTSIZE; + 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; - this.showLegend = isLegendGraphStyle; - } else { - // Leave current value as is + if (data.getNumberOfColumns() > 4) { + this.colFilter = 4; } - } else { - dst.showLegend = showLegend; + } + else { + throw new Error('Unknown style "' + this.style + '"'); } }; +Graph3d.prototype.getNumberOfRows = function(data) { + return data.length; +} + -Graph3d.prototype._setStyle = function(style, dst) { - if (style === undefined) { - return; // Nothing to do +Graph3d.prototype.getNumberOfColumns = function(data) { + var counter = 0; + for (var column in data[0]) { + if (data[0].hasOwnProperty(column)) { + counter++; + } } - - 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, dst) { - var fill = 'white'; - var stroke = 'gray'; - var strokeWidth = 1; - - if (typeof(backgroundColor) === 'string') { - fill = backgroundColor; - stroke = 'none'; - strokeWidth = 0; - } - else if (typeof(backgroundColor) === 'object') { - if (backgroundColor.fill !== undefined) fill = backgroundColor.fill; - if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke; - if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth; - } - else { - throw new Error('Unsupported type of backgroundColor'); - } - - dst.frame.style.backgroundColor = fill; - dst.frame.style.borderColor = stroke; - dst.frame.style.borderWidth = strokeWidth + 'px'; - dst.frame.style.borderStyle = 'solid'; -}; - - -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' - * @return {Number} styleNumber Enumeration value representing the style, or -1 - * when not found - */ -Graph3d.prototype._getStyleNumber = function(styleName) { - switch (styleName) { - case 'dot': return Graph3d.STYLE.DOT; - case 'dot-line': return Graph3d.STYLE.DOTLINE; - case 'dot-color': return Graph3d.STYLE.DOTCOLOR; - case 'dot-size': return Graph3d.STYLE.DOTSIZE; - case 'line': return Graph3d.STYLE.LINE; - case 'grid': return Graph3d.STYLE.GRID; - case 'surface': return Graph3d.STYLE.SURFACE; - case 'bar': return Graph3d.STYLE.BAR; - case 'bar-color': return Graph3d.STYLE.BARCOLOR; - case 'bar-size': return Graph3d.STYLE.BARSIZE; - } - - return -1; -}; - -/** - * 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; -} - - -Graph3d.prototype.getNumberOfColumns = function(data) { - var counter = 0; - for (var column in data[0]) { - if (data[0].hasOwnProperty(column)) { - counter++; - } - } - return counter; -} + return counter; +} Graph3d.prototype.getDistinctValues = function(data, column) { @@ -1203,16 +855,7 @@ Graph3d.prototype.setOptions = function (options) { this.animationStop(); - if (options !== undefined) { - // retrieve parameter values - - // Handle the parameters which can be simply copied over - safeCopy(options, this, OPTIONKEYS); - safeCopy(options, this, PREFIXEDOPTIONKEYS, 'default'); - - // Handle the more complex ('special') fields - this._setSpecialSettings(options, this); - } + this._setOptions(options); this.setSize(this.width, this.height); @@ -2571,4 +2214,362 @@ function getMouseY (event) { return event.targetTouches[0] && event.targetTouches[0].clientY || 0; } + +// ----------------------------------------------------------------------------- +// Methods for handling settings +// ----------------------------------------------------------------------------- + + +/** + * Field names in the options hash which are of relevance to the user. + * + * Specifically, these are the fields which require no special handling, + * and can be directly copied over. + */ +var OPTIONKEYS = [ + 'width', + 'height', + 'filterLabel', + 'legendLabel', + 'xLabel', + 'yLabel', + 'zLabel', + 'xValueLabel', + 'yValueLabel', + 'zValueLabel', + 'showGrid', + 'showPerspective', + 'showShadow', + 'keepAspectRatio', + 'verticalRatio', + 'showAnimationControls', + 'animationInterval', + 'animationPreload', + 'animationAutoStart', + 'axisColor', + 'gridColor', + 'xCenter', + 'yCenter' +]; + + +/** + * Field names in the options hash which are of relevance to the user. + * + * Same as OPTIONKEYS, but internally these fields are stored with + * prefix 'default' in the name. + */ +var PREFIXEDOPTIONKEYS = [ + 'xBarWidth', + 'yBarWidth', + 'valueMin', + 'valueMax', + 'xMin', + 'xMax', + 'xStep', + 'yMin', + 'yMax', + 'yStep', + 'zMin', + 'zMax', + 'zStep' +]; + + +/** + * Make first letter of parameter upper case. + * + * Source: http://stackoverflow.com/a/1026087 + */ +function capitalize(str) { + if (str === undefined || str === "") { + return str; + } + + return str.charAt(0).toUpperCase() + str.slice(1); +} + + +/** + * Add a prefix to a field name, taking style guide into account + */ +function prefixFieldName(prefix, fieldName) { + if (prefix === undefined || prefix === "") { + return fieldName; + } + + return prefix + capitalize(fieldName); +} + + +/** + * Forcibly copy fields from src to dst in a controlled manner. + * + * A given field in dst will always be overwitten. If this field + * is undefined or not present in src, the field in dst will + * be explicitly set to undefined. + * + * The intention here is to be able to reset all option fields. + * + * Only the fields mentioned in array 'fields' will be handled. + * + * @param fields array with names of fields to copy + * @param prefix optional; prefix to use for the target fields. + */ +function forceCopy(src, dst, fields, prefix) { + var srcKey; + var dstKey; + + for (var i in fields) { + srcKey = fields[i]; + dstKey = prefixFieldName(prefix, srcKey); + + dst[dstKey] = src[srcKey]; + } +} + + +/** + * Copy fields from src to dst in a safe and controlled manner. + * + * Only the fields mentioned in array 'fields' will be copied over, + * and only if these are actually defined. + * + * @param fields array with names of fields to copy + * @param prefix optional; prefix to use for the target fields. + */ +function safeCopy(src, dst, fields, prefix) { + var srcKey; + var dstKey; + + for (var i in fields) { + srcKey = fields[i]; + if (src[srcKey] === undefined) continue; + + dstKey = prefixFieldName(prefix, srcKey); + + dst[dstKey] = src[srcKey]; + } +} + + + +Graph3d.prototype._setDefaults = function() { + + // Handle the defaults which can be simply copied over + forceCopy(DEFAULTS, this, OPTIONKEYS); + forceCopy(DEFAULTS, this, PREFIXEDOPTIONKEYS, 'default'); + + // Handle the more complex ('special') fields + this._setSpecialSettings(DEFAULTS, this); + + // Following are internal fields, not part of the user settings + 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.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window? +}; + + +Graph3d.prototype._setOptions = function(options) { + if (options === undefined) { + return; + } + + // Handle the parameters which can be simply copied over + safeCopy(options, this, OPTIONKEYS); + safeCopy(options, this, PREFIXEDOPTIONKEYS, 'default'); + + // Handle the more complex ('special') fields + this._setSpecialSettings(options, this); +}; + + +/** + * 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, dst) { + var fill = 'white'; + var stroke = 'gray'; + var strokeWidth = 1; + + if (typeof(backgroundColor) === 'string') { + fill = backgroundColor; + stroke = 'none'; + strokeWidth = 0; + } + else if (typeof(backgroundColor) === 'object') { + if (backgroundColor.fill !== undefined) fill = backgroundColor.fill; + if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke; + if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth; + } + else { + throw new Error('Unsupported type of backgroundColor'); + } + + dst.frame.style.backgroundColor = fill; + dst.frame.style.borderColor = stroke; + dst.frame.style.borderWidth = strokeWidth + 'px'; + dst.frame.style.borderStyle = 'solid'; +}; + + +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 +// ----------------------------------------------------------------------------- + + module.exports = Graph3d; From 3e84c3557ee04e7ec9dcdde6a24fcb792d2eaabf Mon Sep 17 00:00:00 2001 From: Wim Rijnders Date: Fri, 21 Oct 2016 09:07:18 +0200 Subject: [PATCH 2/3] Moved code for settings to separate module --- lib/graph3d/Graph3d.js | 376 +------------------------------------- lib/graph3d/Settings.js | 388 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 395 insertions(+), 369 deletions(-) create mode 100644 lib/graph3d/Settings.js diff --git a/lib/graph3d/Graph3d.js b/lib/graph3d/Graph3d.js index ccbba1d1..1d8515d6 100644 --- a/lib/graph3d/Graph3d.js +++ b/lib/graph3d/Graph3d.js @@ -7,21 +7,11 @@ var Camera = require('./Camera'); var Filter = require('./Filter'); var Slider = require('./Slider'); var StepNumber = require('./StepNumber'); +var Settings = require('./Settings'); /// 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.STYLE = Settings.STYLE; /** @@ -135,7 +125,7 @@ function Graph3d(container, data, options) { // create a frame and canvas this.create(); - this._setDefaults(); + Settings.setDefaults(this); // the column indexes this.colX = undefined; @@ -302,32 +292,6 @@ Graph3d.prototype._calcTranslations = function(points, sort) { }; - - - -/** - * Retrieve the style index from given styleName - * @param {string} styleName Style name such as 'dot', 'grid', 'dot-line' - * @return {Number} styleNumber Enumeration value representing the style, or -1 - * when not found - */ -Graph3d.prototype._getStyleNumber = function(styleName) { - switch (styleName) { - case 'dot': return Graph3d.STYLE.DOT; - case 'dot-line': return Graph3d.STYLE.DOTLINE; - case 'dot-color': return Graph3d.STYLE.DOTCOLOR; - case 'dot-size': return Graph3d.STYLE.DOTSIZE; - case 'line': return Graph3d.STYLE.LINE; - case 'grid': return Graph3d.STYLE.GRID; - case 'surface': return Graph3d.STYLE.SURFACE; - case 'bar': return Graph3d.STYLE.BAR; - case 'bar-color': return Graph3d.STYLE.BARCOLOR; - case 'bar-size': return Graph3d.STYLE.BARSIZE; - } - - return -1; -}; - /** * Determine the indexes of the data columns, based on the given style and data * @param {DataSet} data @@ -855,7 +819,7 @@ Graph3d.prototype.setOptions = function (options) { this.animationStop(); - this._setOptions(options); + Settings.setOptions(options, this); this.setSize(this.width, this.height); @@ -2216,335 +2180,9 @@ function getMouseY (event) { // ----------------------------------------------------------------------------- -// Methods for handling settings +// Public methods for specific settings // ----------------------------------------------------------------------------- - -/** - * Field names in the options hash which are of relevance to the user. - * - * Specifically, these are the fields which require no special handling, - * and can be directly copied over. - */ -var OPTIONKEYS = [ - 'width', - 'height', - 'filterLabel', - 'legendLabel', - 'xLabel', - 'yLabel', - 'zLabel', - 'xValueLabel', - 'yValueLabel', - 'zValueLabel', - 'showGrid', - 'showPerspective', - 'showShadow', - 'keepAspectRatio', - 'verticalRatio', - 'showAnimationControls', - 'animationInterval', - 'animationPreload', - 'animationAutoStart', - 'axisColor', - 'gridColor', - 'xCenter', - 'yCenter' -]; - - -/** - * Field names in the options hash which are of relevance to the user. - * - * Same as OPTIONKEYS, but internally these fields are stored with - * prefix 'default' in the name. - */ -var PREFIXEDOPTIONKEYS = [ - 'xBarWidth', - 'yBarWidth', - 'valueMin', - 'valueMax', - 'xMin', - 'xMax', - 'xStep', - 'yMin', - 'yMax', - 'yStep', - 'zMin', - 'zMax', - 'zStep' -]; - - -/** - * Make first letter of parameter upper case. - * - * Source: http://stackoverflow.com/a/1026087 - */ -function capitalize(str) { - if (str === undefined || str === "") { - return str; - } - - return str.charAt(0).toUpperCase() + str.slice(1); -} - - -/** - * Add a prefix to a field name, taking style guide into account - */ -function prefixFieldName(prefix, fieldName) { - if (prefix === undefined || prefix === "") { - return fieldName; - } - - return prefix + capitalize(fieldName); -} - - -/** - * Forcibly copy fields from src to dst in a controlled manner. - * - * A given field in dst will always be overwitten. If this field - * is undefined or not present in src, the field in dst will - * be explicitly set to undefined. - * - * The intention here is to be able to reset all option fields. - * - * Only the fields mentioned in array 'fields' will be handled. - * - * @param fields array with names of fields to copy - * @param prefix optional; prefix to use for the target fields. - */ -function forceCopy(src, dst, fields, prefix) { - var srcKey; - var dstKey; - - for (var i in fields) { - srcKey = fields[i]; - dstKey = prefixFieldName(prefix, srcKey); - - dst[dstKey] = src[srcKey]; - } -} - - -/** - * Copy fields from src to dst in a safe and controlled manner. - * - * Only the fields mentioned in array 'fields' will be copied over, - * and only if these are actually defined. - * - * @param fields array with names of fields to copy - * @param prefix optional; prefix to use for the target fields. - */ -function safeCopy(src, dst, fields, prefix) { - var srcKey; - var dstKey; - - for (var i in fields) { - srcKey = fields[i]; - if (src[srcKey] === undefined) continue; - - dstKey = prefixFieldName(prefix, srcKey); - - dst[dstKey] = src[srcKey]; - } -} - - - -Graph3d.prototype._setDefaults = function() { - - // Handle the defaults which can be simply copied over - forceCopy(DEFAULTS, this, OPTIONKEYS); - forceCopy(DEFAULTS, this, PREFIXEDOPTIONKEYS, 'default'); - - // Handle the more complex ('special') fields - this._setSpecialSettings(DEFAULTS, this); - - // Following are internal fields, not part of the user settings - 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.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window? -}; - - -Graph3d.prototype._setOptions = function(options) { - if (options === undefined) { - return; - } - - // Handle the parameters which can be simply copied over - safeCopy(options, this, OPTIONKEYS); - safeCopy(options, this, PREFIXEDOPTIONKEYS, 'default'); - - // Handle the more complex ('special') fields - this._setSpecialSettings(options, this); -}; - - -/** - * 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, dst) { - var fill = 'white'; - var stroke = 'gray'; - var strokeWidth = 1; - - if (typeof(backgroundColor) === 'string') { - fill = backgroundColor; - stroke = 'none'; - strokeWidth = 0; - } - else if (typeof(backgroundColor) === 'object') { - if (backgroundColor.fill !== undefined) fill = backgroundColor.fill; - if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke; - if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth; - } - else { - throw new Error('Unsupported type of backgroundColor'); - } - - dst.frame.style.backgroundColor = fill; - dst.frame.style.borderColor = stroke; - dst.frame.style.borderWidth = strokeWidth + 'px'; - dst.frame.style.borderStyle = 'solid'; -}; - - -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 @@ -2562,13 +2200,13 @@ Graph3d.prototype._setCameraPosition = function(cameraPosition, dst) { * Optional, can be left undefined. */ Graph3d.prototype.setCameraPosition = function(pos) { - this._setCameraPosition(pos, this); + Settings.setCameraPosition(pos, this); this.redraw(); }; // ----------------------------------------------------------------------------- -// End methods for handling settings +// End public methods for specific settings // ----------------------------------------------------------------------------- diff --git a/lib/graph3d/Settings.js b/lib/graph3d/Settings.js new file mode 100644 index 00000000..c8663a18 --- /dev/null +++ b/lib/graph3d/Settings.js @@ -0,0 +1,388 @@ +//////////////////////////////////////////////////////////////////////////////// +// This modules handles the options for Graph3d. +// +//////////////////////////////////////////////////////////////////////////////// + +// 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 +}; + + +// The string representations of the styles +var STYLENAME = { + 'dot' : STYLE.DOT, + 'dot-line' : STYLE.DOTLINE, + 'dot-color': STYLE.DOTCOLOR, + 'dot-size' : STYLE.DOTSIZE, + 'line' : STYLE.LINE, + 'grid' : STYLE.GRID, + 'surface' : STYLE.SURFACE, + 'bar' : STYLE.BAR, + 'bar-color': STYLE.BARCOLOR, + 'bar-size' : STYLE.BARSIZE +}; + + +/** + * Field names in the options hash which are of relevance to the user. + * + * Specifically, these are the fields which require no special handling, + * and can be directly copied over. + */ +var OPTIONKEYS = [ + 'width', + 'height', + 'filterLabel', + 'legendLabel', + 'xLabel', + 'yLabel', + 'zLabel', + 'xValueLabel', + 'yValueLabel', + 'zValueLabel', + 'showGrid', + 'showPerspective', + 'showShadow', + 'keepAspectRatio', + 'verticalRatio', + 'showAnimationControls', + 'animationInterval', + 'animationPreload', + 'animationAutoStart', + 'axisColor', + 'gridColor', + 'xCenter', + 'yCenter' +]; + + +/** + * Field names in the options hash which are of relevance to the user. + * + * Same as OPTIONKEYS, but internally these fields are stored with + * prefix 'default' in the name. + */ +var PREFIXEDOPTIONKEYS = [ + 'xBarWidth', + 'yBarWidth', + 'valueMin', + 'valueMax', + 'xMin', + 'xMax', + 'xStep', + 'yMin', + 'yMax', + 'yStep', + 'zMin', + 'zMax', + 'zStep' +]; + + +/** + * Make first letter of parameter upper case. + * + * Source: http://stackoverflow.com/a/1026087 + */ +function capitalize(str) { + if (str === undefined || str === "") { + return str; + } + + return str.charAt(0).toUpperCase() + str.slice(1); +} + + +/** + * Add a prefix to a field name, taking style guide into account + */ +function prefixFieldName(prefix, fieldName) { + if (prefix === undefined || prefix === "") { + return fieldName; + } + + return prefix + capitalize(fieldName); +} + + +/** + * Forcibly copy fields from src to dst in a controlled manner. + * + * A given field in dst will always be overwitten. If this field + * is undefined or not present in src, the field in dst will + * be explicitly set to undefined. + * + * The intention here is to be able to reset all option fields. + * + * Only the fields mentioned in array 'fields' will be handled. + * + * @param fields array with names of fields to copy + * @param prefix optional; prefix to use for the target fields. + */ +function forceCopy(src, dst, fields, prefix) { + var srcKey; + var dstKey; + + for (var i in fields) { + srcKey = fields[i]; + dstKey = prefixFieldName(prefix, srcKey); + + dst[dstKey] = src[srcKey]; + } +} + + +/** + * Copy fields from src to dst in a safe and controlled manner. + * + * Only the fields mentioned in array 'fields' will be copied over, + * and only if these are actually defined. + * + * @param fields array with names of fields to copy + * @param prefix optional; prefix to use for the target fields. + */ +function safeCopy(src, dst, fields, prefix) { + var srcKey; + var dstKey; + + for (var i in fields) { + srcKey = fields[i]; + if (src[srcKey] === undefined) continue; + + dstKey = prefixFieldName(prefix, srcKey); + + dst[dstKey] = src[srcKey]; + } +} + + +function setDefaults(dst) { + // Handle the defaults which can be simply copied over + forceCopy(DEFAULTS, dst, OPTIONKEYS); + forceCopy(DEFAULTS, dst, PREFIXEDOPTIONKEYS, 'default'); + + // Handle the more complex ('special') fields + setSpecialSettings(DEFAULTS, dst); + + // Following are internal fields, not part of the user settings + dst.margin = 10; // px + dst.showGrayBottom = false; // TODO: this does not work correctly + dst.showTooltip = false; + dst.dotSizeRatio = 0.02; // size of the dots as a fraction of the graph width + dst.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window? +} + + +function setOptions(options, dst) { + if (options === undefined) { + return; + } + + // Handle the parameters which can be simply copied over + safeCopy(options, dst, OPTIONKEYS); + safeCopy(options, dst, PREFIXEDOPTIONKEYS, 'default'); + + // Handle the more complex ('special') fields + setSpecialSettings(options, dst); +} + + +/** + * Special handling for certain parameters + * + * 'Special' here means: setting requires more than a simple copy + */ +function setSpecialSettings(src, dst) { + if (src.backgroundColor !== undefined) { + setBackgroundColor(src.backgroundColor, dst); + } + + setDataColor(src.dataColor, dst); + setStyle(src.style, dst); + setShowLegend(src.showLegend, dst); + 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. + */ +function setShowLegend(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 = dst.style === STYLE.DOTCOLOR + || dst.style === STYLE.DOTSIZE; + + dst.showLegend = isLegendGraphStyle; + } else { + // Leave current value as is + } + } else { + dst.showLegend = showLegend; + } +} + + +/** + * Retrieve the style index from given styleName + * @param {string} styleName Style name such as 'dot', 'grid', 'dot-line' + * @return {Number} styleNumber Enumeration value representing the style, or -1 + * when not found + */ +function getStyleNumberByName(styleName) { + var number = STYLENAME[stylename]; + + if (number === undefined) { + return -1; + } + + return number; +} + + +/** + * Check if given number is a valid style number. + * + * @return true if valid, false otherwise + */ +function checkStyleNumber(style) { + var valid = false; + + for (var n in STYLE) { + if (STYLE[n] === style) { + valid = true; + break; + } + } + + return valid; +} + + +function setStyle(style, dst) { + if (style === undefined) { + return; // Nothing to do + } + + var styleNumber; + + if (typeof style === 'string') { + styleNumber = getStyleNumberByName(style); + + if (styleNumber === -1 ) { + throw new Error('Style \'' + style + '\' is invalid'); + } + } else { + // Do a pedantic check on style number value + if (!checkStyleNumber(style)) { + 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 + */ +function setBackgroundColor(backgroundColor, dst) { + var fill = 'white'; + var stroke = 'gray'; + var strokeWidth = 1; + + if (typeof(backgroundColor) === 'string') { + fill = backgroundColor; + stroke = 'none'; + strokeWidth = 0; + } + else if (typeof(backgroundColor) === 'object') { + if (backgroundColor.fill !== undefined) fill = backgroundColor.fill; + if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke; + if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth; + } + else { + throw new Error('Unsupported type of backgroundColor'); + } + + dst.frame.style.backgroundColor = fill; + dst.frame.style.borderColor = stroke; + dst.frame.style.borderWidth = strokeWidth + 'px'; + dst.frame.style.borderStyle = 'solid'; +} + + +function setDataColor(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; + } + } +} + + +function setCameraPosition(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); +} + + + + +module.exports.STYLE = STYLE; +module.exports.setDefaults = setDefaults; +module.exports.setOptions = setOptions; +module.exports.setCameraPosition = setCameraPosition; From baed96ee7ce2dd0344f57f61bde099894bb0473b Mon Sep 17 00:00:00 2001 From: Wim Rijnders Date: Fri, 21 Oct 2016 09:58:51 +0200 Subject: [PATCH 3/3] Fixes and hardening for Settings.js --- lib/graph3d/Graph3d.js | 3 +-- lib/graph3d/Settings.js | 60 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 56 insertions(+), 7 deletions(-) diff --git a/lib/graph3d/Graph3d.js b/lib/graph3d/Graph3d.js index 1d8515d6..fa8e2b37 100644 --- a/lib/graph3d/Graph3d.js +++ b/lib/graph3d/Graph3d.js @@ -94,7 +94,6 @@ var DEFAULTS = { }; - // ----------------------------------------------------------------------------- // Class Graph3d // ----------------------------------------------------------------------------- @@ -125,7 +124,7 @@ function Graph3d(container, data, options) { // create a frame and canvas this.create(); - Settings.setDefaults(this); + Settings.setDefaults(DEFAULTS, this); // the column indexes this.colX = undefined; diff --git a/lib/graph3d/Settings.js b/lib/graph3d/Settings.js index c8663a18..f1b7da6a 100644 --- a/lib/graph3d/Settings.js +++ b/lib/graph3d/Settings.js @@ -2,6 +2,9 @@ // This modules handles the options for Graph3d. // //////////////////////////////////////////////////////////////////////////////// +var Camera = require('./Camera'); +var Point3d = require('./Point3d'); + // enumerate the available styles var STYLE = { @@ -89,6 +92,26 @@ var PREFIXEDOPTIONKEYS = [ ]; +// Placeholder for DEFAULTS reference +var DEFAULTS = undefined; + + +/** + * Check if given hash is empty. + * + * Source: http://stackoverflow.com/a/679937 + */ +function isEmpty(obj) { + for(var prop in obj) { + if (obj.hasOwnProperty(prop)) + return false; + } + + return true; +} + + + /** * Make first letter of parameter upper case. * @@ -166,13 +189,32 @@ function safeCopy(src, dst, fields, prefix) { } -function setDefaults(dst) { +/** + * Initialize dst with the values in src. + * + * src is the hash with the default values. + * A reference DEFAULTS to this hash is stored locally for + * further handling. + * + * For now, dst is assumed to be a Graph3d instance. + */ +function setDefaults(src, dst) { + if (src === undefined || isEmpty(src)) { + throw new Error('No DEFAULTS passed'); + } + if (dst === undefined) { + throw new Error('No dst passed'); + } + + // Remember defaults for future reference + DEFAULTS = src; + // Handle the defaults which can be simply copied over - forceCopy(DEFAULTS, dst, OPTIONKEYS); - forceCopy(DEFAULTS, dst, PREFIXEDOPTIONKEYS, 'default'); + forceCopy(src, dst, OPTIONKEYS); + forceCopy(src, dst, PREFIXEDOPTIONKEYS, 'default'); // Handle the more complex ('special') fields - setSpecialSettings(DEFAULTS, dst); + setSpecialSettings(src, dst); // Following are internal fields, not part of the user settings dst.margin = 10; // px @@ -187,6 +229,14 @@ function setOptions(options, dst) { if (options === undefined) { return; } + if (dst === undefined) { + throw new Error('No dst passed'); + } + + if (DEFAULTS === undefined || isEmpty(DEFAULTS)) { + throw new Error('DEFAULTS not set for module Settings'); + } + // Handle the parameters which can be simply copied over safeCopy(options, dst, OPTIONKEYS); @@ -253,7 +303,7 @@ function setShowLegend(showLegend, dst) { * when not found */ function getStyleNumberByName(styleName) { - var number = STYLENAME[stylename]; + var number = STYLENAME[styleName]; if (number === undefined) { return -1;