vis.js is a dynamic, browser-based visualization library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

388 lines
8.9 KiB

  1. ////////////////////////////////////////////////////////////////////////////////
  2. // This modules handles the options for Graph3d.
  3. //
  4. ////////////////////////////////////////////////////////////////////////////////
  5. // enumerate the available styles
  6. var STYLE = {
  7. BAR : 0,
  8. BARCOLOR: 1,
  9. BARSIZE : 2,
  10. DOT : 3,
  11. DOTLINE : 4,
  12. DOTCOLOR: 5,
  13. DOTSIZE : 6,
  14. GRID : 7,
  15. LINE : 8,
  16. SURFACE : 9
  17. };
  18. // The string representations of the styles
  19. var STYLENAME = {
  20. 'dot' : STYLE.DOT,
  21. 'dot-line' : STYLE.DOTLINE,
  22. 'dot-color': STYLE.DOTCOLOR,
  23. 'dot-size' : STYLE.DOTSIZE,
  24. 'line' : STYLE.LINE,
  25. 'grid' : STYLE.GRID,
  26. 'surface' : STYLE.SURFACE,
  27. 'bar' : STYLE.BAR,
  28. 'bar-color': STYLE.BARCOLOR,
  29. 'bar-size' : STYLE.BARSIZE
  30. };
  31. /**
  32. * Field names in the options hash which are of relevance to the user.
  33. *
  34. * Specifically, these are the fields which require no special handling,
  35. * and can be directly copied over.
  36. */
  37. var OPTIONKEYS = [
  38. 'width',
  39. 'height',
  40. 'filterLabel',
  41. 'legendLabel',
  42. 'xLabel',
  43. 'yLabel',
  44. 'zLabel',
  45. 'xValueLabel',
  46. 'yValueLabel',
  47. 'zValueLabel',
  48. 'showGrid',
  49. 'showPerspective',
  50. 'showShadow',
  51. 'keepAspectRatio',
  52. 'verticalRatio',
  53. 'showAnimationControls',
  54. 'animationInterval',
  55. 'animationPreload',
  56. 'animationAutoStart',
  57. 'axisColor',
  58. 'gridColor',
  59. 'xCenter',
  60. 'yCenter'
  61. ];
  62. /**
  63. * Field names in the options hash which are of relevance to the user.
  64. *
  65. * Same as OPTIONKEYS, but internally these fields are stored with
  66. * prefix 'default' in the name.
  67. */
  68. var PREFIXEDOPTIONKEYS = [
  69. 'xBarWidth',
  70. 'yBarWidth',
  71. 'valueMin',
  72. 'valueMax',
  73. 'xMin',
  74. 'xMax',
  75. 'xStep',
  76. 'yMin',
  77. 'yMax',
  78. 'yStep',
  79. 'zMin',
  80. 'zMax',
  81. 'zStep'
  82. ];
  83. /**
  84. * Make first letter of parameter upper case.
  85. *
  86. * Source: http://stackoverflow.com/a/1026087
  87. */
  88. function capitalize(str) {
  89. if (str === undefined || str === "") {
  90. return str;
  91. }
  92. return str.charAt(0).toUpperCase() + str.slice(1);
  93. }
  94. /**
  95. * Add a prefix to a field name, taking style guide into account
  96. */
  97. function prefixFieldName(prefix, fieldName) {
  98. if (prefix === undefined || prefix === "") {
  99. return fieldName;
  100. }
  101. return prefix + capitalize(fieldName);
  102. }
  103. /**
  104. * Forcibly copy fields from src to dst in a controlled manner.
  105. *
  106. * A given field in dst will always be overwitten. If this field
  107. * is undefined or not present in src, the field in dst will
  108. * be explicitly set to undefined.
  109. *
  110. * The intention here is to be able to reset all option fields.
  111. *
  112. * Only the fields mentioned in array 'fields' will be handled.
  113. *
  114. * @param fields array with names of fields to copy
  115. * @param prefix optional; prefix to use for the target fields.
  116. */
  117. function forceCopy(src, dst, fields, prefix) {
  118. var srcKey;
  119. var dstKey;
  120. for (var i in fields) {
  121. srcKey = fields[i];
  122. dstKey = prefixFieldName(prefix, srcKey);
  123. dst[dstKey] = src[srcKey];
  124. }
  125. }
  126. /**
  127. * Copy fields from src to dst in a safe and controlled manner.
  128. *
  129. * Only the fields mentioned in array 'fields' will be copied over,
  130. * and only if these are actually defined.
  131. *
  132. * @param fields array with names of fields to copy
  133. * @param prefix optional; prefix to use for the target fields.
  134. */
  135. function safeCopy(src, dst, fields, prefix) {
  136. var srcKey;
  137. var dstKey;
  138. for (var i in fields) {
  139. srcKey = fields[i];
  140. if (src[srcKey] === undefined) continue;
  141. dstKey = prefixFieldName(prefix, srcKey);
  142. dst[dstKey] = src[srcKey];
  143. }
  144. }
  145. function setDefaults(dst) {
  146. // Handle the defaults which can be simply copied over
  147. forceCopy(DEFAULTS, dst, OPTIONKEYS);
  148. forceCopy(DEFAULTS, dst, PREFIXEDOPTIONKEYS, 'default');
  149. // Handle the more complex ('special') fields
  150. setSpecialSettings(DEFAULTS, dst);
  151. // Following are internal fields, not part of the user settings
  152. dst.margin = 10; // px
  153. dst.showGrayBottom = false; // TODO: this does not work correctly
  154. dst.showTooltip = false;
  155. dst.dotSizeRatio = 0.02; // size of the dots as a fraction of the graph width
  156. dst.eye = new Point3d(0, 0, -1); // TODO: set eye.z about 3/4 of the width of the window?
  157. }
  158. function setOptions(options, dst) {
  159. if (options === undefined) {
  160. return;
  161. }
  162. // Handle the parameters which can be simply copied over
  163. safeCopy(options, dst, OPTIONKEYS);
  164. safeCopy(options, dst, PREFIXEDOPTIONKEYS, 'default');
  165. // Handle the more complex ('special') fields
  166. setSpecialSettings(options, dst);
  167. }
  168. /**
  169. * Special handling for certain parameters
  170. *
  171. * 'Special' here means: setting requires more than a simple copy
  172. */
  173. function setSpecialSettings(src, dst) {
  174. if (src.backgroundColor !== undefined) {
  175. setBackgroundColor(src.backgroundColor, dst);
  176. }
  177. setDataColor(src.dataColor, dst);
  178. setStyle(src.style, dst);
  179. setShowLegend(src.showLegend, dst);
  180. setCameraPosition(src.cameraPosition, dst);
  181. // As special fields go, this is an easy one; just a translation of the name.
  182. // Can't use this.tooltip directly, because that field exists internally
  183. if (src.tooltip !== undefined) {
  184. dst.showTooltip = src.tooltip;
  185. }
  186. }
  187. /**
  188. * Set the value of setting 'showLegend'
  189. *
  190. * This depends on the value of the style fields, so it must be called
  191. * after the style field has been initialized.
  192. */
  193. function setShowLegend(showLegend, dst) {
  194. if (showLegend === undefined) {
  195. // If the default was auto, make a choice for this field
  196. var isAutoByDefault = (DEFAULTS.showLegend === undefined);
  197. if (isAutoByDefault) {
  198. // these styles default to having legends
  199. var isLegendGraphStyle = dst.style === STYLE.DOTCOLOR
  200. || dst.style === STYLE.DOTSIZE;
  201. dst.showLegend = isLegendGraphStyle;
  202. } else {
  203. // Leave current value as is
  204. }
  205. } else {
  206. dst.showLegend = showLegend;
  207. }
  208. }
  209. /**
  210. * Retrieve the style index from given styleName
  211. * @param {string} styleName Style name such as 'dot', 'grid', 'dot-line'
  212. * @return {Number} styleNumber Enumeration value representing the style, or -1
  213. * when not found
  214. */
  215. function getStyleNumberByName(styleName) {
  216. var number = STYLENAME[stylename];
  217. if (number === undefined) {
  218. return -1;
  219. }
  220. return number;
  221. }
  222. /**
  223. * Check if given number is a valid style number.
  224. *
  225. * @return true if valid, false otherwise
  226. */
  227. function checkStyleNumber(style) {
  228. var valid = false;
  229. for (var n in STYLE) {
  230. if (STYLE[n] === style) {
  231. valid = true;
  232. break;
  233. }
  234. }
  235. return valid;
  236. }
  237. function setStyle(style, dst) {
  238. if (style === undefined) {
  239. return; // Nothing to do
  240. }
  241. var styleNumber;
  242. if (typeof style === 'string') {
  243. styleNumber = getStyleNumberByName(style);
  244. if (styleNumber === -1 ) {
  245. throw new Error('Style \'' + style + '\' is invalid');
  246. }
  247. } else {
  248. // Do a pedantic check on style number value
  249. if (!checkStyleNumber(style)) {
  250. throw new Error('Style \'' + style + '\' is invalid');
  251. }
  252. styleNumber = style;
  253. }
  254. dst.style = styleNumber;
  255. }
  256. /**
  257. * Set the background styling for the graph
  258. * @param {string | {fill: string, stroke: string, strokeWidth: string}} backgroundColor
  259. */
  260. function setBackgroundColor(backgroundColor, dst) {
  261. var fill = 'white';
  262. var stroke = 'gray';
  263. var strokeWidth = 1;
  264. if (typeof(backgroundColor) === 'string') {
  265. fill = backgroundColor;
  266. stroke = 'none';
  267. strokeWidth = 0;
  268. }
  269. else if (typeof(backgroundColor) === 'object') {
  270. if (backgroundColor.fill !== undefined) fill = backgroundColor.fill;
  271. if (backgroundColor.stroke !== undefined) stroke = backgroundColor.stroke;
  272. if (backgroundColor.strokeWidth !== undefined) strokeWidth = backgroundColor.strokeWidth;
  273. }
  274. else {
  275. throw new Error('Unsupported type of backgroundColor');
  276. }
  277. dst.frame.style.backgroundColor = fill;
  278. dst.frame.style.borderColor = stroke;
  279. dst.frame.style.borderWidth = strokeWidth + 'px';
  280. dst.frame.style.borderStyle = 'solid';
  281. }
  282. function setDataColor(dataColor, dst) {
  283. if (dataColor === undefined) {
  284. return; // Nothing to do
  285. }
  286. if (dst.dataColor === undefined) {
  287. dst.dataColor = {};
  288. }
  289. if (typeof dataColor === 'string') {
  290. dst.dataColor.fill = dataColor;
  291. dst.dataColor.stroke = dataColor;
  292. }
  293. else {
  294. if (dataColor.fill) {
  295. dst.dataColor.fill = dataColor.fill;
  296. }
  297. if (dataColor.stroke) {
  298. dst.dataColor.stroke = dataColor.stroke;
  299. }
  300. if (dataColor.strokeWidth !== undefined) {
  301. dst.dataColor.strokeWidth = dataColor.strokeWidth;
  302. }
  303. }
  304. }
  305. function setCameraPosition(cameraPosition, dst) {
  306. var camPos = cameraPosition;
  307. if (camPos === undefined) {
  308. return;
  309. }
  310. if (dst.camera === undefined) {
  311. dst.camera = new Camera();
  312. }
  313. dst.camera.setArmRotation(camPos.horizontal, camPos.vertical);
  314. dst.camera.setArmLength(camPos.distance);
  315. }
  316. module.exports.STYLE = STYLE;
  317. module.exports.setDefaults = setDefaults;
  318. module.exports.setOptions = setOptions;
  319. module.exports.setCameraPosition = setCameraPosition;