Browse Source

Moved code for settings to separate module

codeClimate
Wim Rijnders 8 years ago
parent
commit
3e84c3557e
2 changed files with 395 additions and 369 deletions
  1. +7
    -369
      lib/graph3d/Graph3d.js
  2. +388
    -0
      lib/graph3d/Settings.js

+ 7
- 369
lib/graph3d/Graph3d.js View File

@ -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
// -----------------------------------------------------------------------------

+ 388
- 0
lib/graph3d/Settings.js View File

@ -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;

Loading…
Cancel
Save