Plugin/stanford/index.ts

  1. /**
  2. * Copyright (c) 2017 ~ present NAVER Corp.
  3. * billboard.js project is licensed under the MIT license
  4. */
  5. // @ts-nocheck
  6. import {hsl as d3Hsl} from "d3-color";
  7. import {interpolateHslLong as d3InterpolateHslLong} from "d3-interpolate";
  8. import {scaleSequentialLog as d3ScaleSequentialLog} from "d3-scale";
  9. import {$TOOLTIP} from "../../config/classes";
  10. import {loadConfig} from "../../config/config";
  11. import Plugin from "../Plugin";
  12. import ColorScale from "./ColorScale";
  13. import Elements from "./Elements";
  14. import Options from "./Options";
  15. import {compareEpochs, isEmpty, isFunction, pointInRegion} from "./util";
  16. /**
  17. * Stanford diagram plugin
  18. * - **NOTE:**
  19. * - Plugins aren't built-in. Need to be loaded or imported to be used.
  20. * - Non required modules from billboard.js core, need to be installed separately.
  21. * - Is preferable use `scatter` as data.type
  22. * - **Required modules:**
  23. * - [d3-selection](https://github.com/d3/d3-selection)
  24. * - [d3-interpolate](https://github.com/d3/d3-interpolate)
  25. * - [d3-color](https://github.com/d3/d3-color)
  26. * - [d3-scale](https://github.com/d3/d3-scale)
  27. * - [d3-brush](https://github.com/d3/d3-brush)
  28. * - [d3-axis](https://github.com/d3/d3-axis)
  29. * - [d3-format](https://github.com/d3/d3-format)
  30. * @class plugin-stanford
  31. * @requires d3-selection
  32. * @requires d3-interpolate
  33. * @requires d3-color
  34. * @requires d3-scale
  35. * @requires d3-brush
  36. * @requires d3-axis
  37. * @requires d3-format
  38. * @param {object} options Stanford plugin options
  39. * @augments Plugin
  40. * @returns {Stanford}
  41. * @example
  42. * // Plugin must be loaded before the use.
  43. * <script src="$YOUR_PATH/plugin/billboardjs-plugin-stanford.js"></script>
  44. *
  45. * var chart = bb.generate({
  46. * data: {
  47. * columns: [ ... ],
  48. * type: "scatter"
  49. * }
  50. * ...
  51. * plugins: [
  52. * new bb.plugin.stanford({
  53. * colors: d3.interpolateHslLong(
  54. * d3.hsl(250, 1, 0.5), d3.hsl(0, 1, 0.5)
  55. * ),
  56. * epochs: [ 1, 1, 2, 2, ... ],
  57. * lines: [
  58. * { x1: 0, y1: 0, x2: 65, y2: 65, class: "line1" },
  59. * { x1: 0, x2: 65, y1: 40, y2: 40, class: "line2" }
  60. * ],
  61. * scale: {
  62. * max: 10000,
  63. * min: 1,
  64. * width: 500,
  65. * format: 'pow10',
  66. * },
  67. * padding: {
  68. * top: 15,
  69. * right: 0,
  70. * bottom: 0,
  71. * left: 0
  72. * },
  73. * regions: [
  74. * {
  75. * points: [ // add points counter-clockwise
  76. * { x: 0, y: 0 },
  77. * { x: 40, y: 40 },
  78. * { x: 0, y: 40 }
  79. * ],
  80. * text: function (value, percentage) {
  81. * return `Normal Operations: ${value} (${percentage}%)`;
  82. * },
  83. * opacity: 0.2, // 0 to 1
  84. * class: "test-polygon1"
  85. * },
  86. * ...
  87. * ]
  88. * }
  89. * ]
  90. * });
  91. * @example
  92. * import {bb} from "billboard.js";
  93. * import Stanford from "billboard.js/dist/billboardjs-plugin-stanford";
  94. *
  95. * bb.generate({
  96. * plugins: [
  97. * new Stanford({ ... })
  98. * ]
  99. * })
  100. */
  101. export default class Stanford extends Plugin {
  102. private config;
  103. private colorScale;
  104. private elements;
  105. constructor(options) {
  106. super(options);
  107. this.config = new Options();
  108. return this;
  109. }
  110. $beforeInit(): void {
  111. const {$$} = this;
  112. // override on config values & methods
  113. $$.config.data_xSort = false;
  114. $$.isMultipleX = () => true;
  115. $$.showGridFocus = () => {};
  116. $$.labelishData = d => d.values;
  117. $$.opacityForCircle = () => 1;
  118. const getCurrentPadding = $$.getCurrentPadding.bind($$);
  119. $$.getCurrentPadding = () => {
  120. const padding = getCurrentPadding();
  121. padding.right += this.colorScale ? this.colorScale.getColorScalePadding() : 0;
  122. return padding;
  123. };
  124. }
  125. $init(): void {
  126. const {$$} = this;
  127. loadConfig.call(this, this.options);
  128. $$.color = this.getStanfordPointColor.bind($$);
  129. this.colorScale = new ColorScale(this);
  130. this.elements = new Elements(this);
  131. this.convertData();
  132. this.initStanfordData();
  133. this.setStanfordTooltip();
  134. this.colorScale.drawColorScale();
  135. $$.right += this.colorScale ? this.colorScale.getColorScalePadding() : 0;
  136. this.$redraw();
  137. }
  138. $redraw(duration?: number): void {
  139. this.colorScale?.drawColorScale();
  140. this.elements?.updateStanfordElements(duration);
  141. }
  142. getOptions(): Options {
  143. return new Options();
  144. }
  145. convertData(): void {
  146. const data = this.$$.data.targets;
  147. const epochs = this.options.epochs;
  148. data.forEach(d => {
  149. d.values.forEach((v, i) => {
  150. v.epochs = epochs[i];
  151. });
  152. d.minEpochs = undefined;
  153. d.maxEpochs = undefined;
  154. d.colors = undefined;
  155. d.colorscale = undefined;
  156. });
  157. }
  158. initStanfordData(): void {
  159. const {config} = this;
  160. const target = this.$$.data.targets[0];
  161. // TODO STANFORD see if (data.js -> orderTargets)+ can be used instead
  162. // Make larger values appear on top
  163. target.values.sort(compareEpochs);
  164. // Get array of epochs
  165. const epochs = target.values.map(a => a.epochs);
  166. target.minEpochs = !isNaN(config.scale_min) ? config.scale_min : Math.min(...epochs);
  167. target.maxEpochs = !isNaN(config.scale_max) ? config.scale_max : Math.max(...epochs);
  168. target.colors = isFunction(config.colors) ?
  169. config.colors :
  170. d3InterpolateHslLong(d3Hsl(250, 1, 0.5), d3Hsl(0, 1, 0.5));
  171. target.colorscale = d3ScaleSequentialLog(target.colors)
  172. .domain([target.minEpochs, target.maxEpochs]);
  173. }
  174. getStanfordPointColor(d) {
  175. const target = this.data.targets[0];
  176. return target.colorscale(d.epochs);
  177. }
  178. setStanfordTooltip(): string | undefined {
  179. const {config} = this.$$;
  180. if (isEmpty(config.tooltip_contents)) {
  181. config.tooltip_contents = function(d, defaultTitleFormat, defaultValueFormat, color) {
  182. const {data_x} = config;
  183. let html = `<table class="${$TOOLTIP.tooltip}"><tbody>`;
  184. d.forEach(v => {
  185. const {id = "", value = 0, epochs = 0, x = ""} = v;
  186. html += `<tr>
  187. <th>${data_x || ""}</th>
  188. <th class="value">${defaultTitleFormat(x)}</th>
  189. </tr>
  190. <tr>
  191. <th>${v.id}</th>
  192. <th class="value">${defaultValueFormat(value)}</th>
  193. </tr>
  194. <tr class="${$TOOLTIP.tooltipName}-${id}">
  195. <td class="name"><span style="background-color:${
  196. color(v)
  197. }"></span>Epochs</td>
  198. <td class="value">${defaultValueFormat(epochs)}</td>
  199. </tr>`;
  200. });
  201. return `${html}</tbody></table>`;
  202. };
  203. }
  204. }
  205. countEpochsInRegion(region): {value: number, percentage: number} {
  206. const $$ = this;
  207. const target = $$.data.targets[0];
  208. const total = target.values.reduce(
  209. (accumulator, currentValue) => accumulator + Number(currentValue.epochs),
  210. 0
  211. );
  212. const value = target.values.reduce((accumulator, currentValue) => {
  213. if (pointInRegion(currentValue, region)) {
  214. return accumulator + Number(currentValue.epochs);
  215. }
  216. return accumulator;
  217. }, 0);
  218. return {
  219. value,
  220. percentage: value !== 0 ? +(value / total * 100).toFixed(1) : 0
  221. };
  222. }
  223. }