ScaleUtil.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. 'use strict';
  2. /**
  3. * Licensed Materials - Property of IBM
  4. * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2016, 2017
  5. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  6. */
  7. define(['../../lib/@waca/core-client/js/core-client/ui/core/Class'], function (Class) {
  8. var ScaleUtil = {};
  9. /**
  10. * Functions used to handle calculation of scale options for infographic display functionality
  11. *
  12. */
  13. ScaleUtil = Class.extend({
  14. // Constants that handles conversion between numeric and text values. Used for readability, as numbers are used for scale slider min/max but they interpret as text values
  15. SCALE_VALUE_FEW: 0,
  16. SCALE_VALUE_DEFAULT: 1,
  17. SCALE_VALUE_MANY: 2,
  18. PERCENT_OPTIMAL_VALUE: 0.1,
  19. PERCENT_SCALE_OPTIONS: [0.01, 0.1, 1],
  20. /**
  21. * Calculates the default scale value for a given input number
  22. * @param rawNum - Input number to be infographically displayed
  23. * @param isPercent - If the raw number is used to represent a percentage infographic
  24. * @returns The default scale value to be used
  25. */
  26. calcOptimalValue: function calcOptimalValue(rawNum, isPercent) {
  27. // If rawNum is null (i.e. no value provided, treat same as zero case to avoid returning errors later on
  28. if (rawNum === null) {
  29. rawNum = 0;
  30. }
  31. // Null if not a number
  32. if (typeof rawNum !== 'number') {
  33. return null;
  34. }
  35. if (isPercent) {
  36. return this.PERCENT_OPTIMAL_VALUE;
  37. }
  38. // Take abs to handle negative numbers
  39. var absNum = Math.abs(rawNum);
  40. var sign = this.getSign(rawNum) || 1;
  41. var numLen = Math.ceil(Math.log(absNum + 1) / Math.LN10);
  42. // Default optimal value is 1.
  43. var optimalValue = 1;
  44. // If the number of digits is greater than 2 than calculate the optimal value
  45. if (numLen > 2) {
  46. optimalValue = Math.pow(10, numLen - 2);
  47. }
  48. return optimalValue * sign;
  49. },
  50. /**
  51. * Gets an object containing a list of scale options as well as the default for a given number
  52. * @param rawNum - Input number to be infographically displayed
  53. * @param isPercent - If the raw number is used to represent a percentage infographic
  54. * @returns An object containing an array of scaling options to be used, as well as the default scale to be shown
  55. */
  56. getScalingProperties: function getScalingProperties(rawNum, isPercent) {
  57. // If rawNum is null (i.e. no value provided, treat same as zero case to avoid returning errors later on
  58. if (rawNum === null) {
  59. rawNum = 0;
  60. }
  61. // Null if not a number
  62. if (typeof rawNum !== 'number') {
  63. return null;
  64. }
  65. if (isPercent) {
  66. return {
  67. availableScales: this.PERCENT_SCALE_OPTIONS,
  68. optimalScale: this.PERCENT_OPTIMAL_VALUE,
  69. scalingOptions: [this.SCALE_VALUE_FEW, this.SCALE_VALUE_DEFAULT, this.SCALE_VALUE_MANY]
  70. };
  71. }
  72. // get the absolute number
  73. var absNum = Math.abs(rawNum);
  74. // get the sign of the number to help handle negative numbers
  75. var sign = this.getSign(rawNum) || 1;
  76. // Get the ideal scale for the given number
  77. var optimalValue = this.calcOptimalValue(absNum);
  78. var availableScales = [optimalValue * sign];
  79. // Only add lower scale option if the default is greater than one
  80. if (optimalValue > 1) {
  81. availableScales.unshift(optimalValue / 10 * sign);
  82. }
  83. // Check if potential high value is valid scale option for input number
  84. var potentialHigh = optimalValue * 10;
  85. if (absNum / potentialHigh >= 1) {
  86. availableScales.push(potentialHigh * sign);
  87. }
  88. // Assemble both parts of final product to be returned to user
  89. var result = {
  90. availableScales: availableScales,
  91. optimalScale: optimalValue * sign
  92. };
  93. result.scalingOptions = this._getScaleOptions(result, isPercent);
  94. return result;
  95. },
  96. /**
  97. * Returns the corresponding min/max values for slider which user utilizes to select preferred scale option
  98. * @param {Number} rawNum - Raw input number which is being used to determine the scale
  99. * @param isPercent - If the raw number is used to represent a percentage infographic
  100. * @returns result object containing min and max value for slider
  101. */
  102. getMinMax: function getMinMax(rawNum, isPercent) {
  103. // If rawNum is null (i.e. no value provided, treat same as zero case to avoid returning errors later on
  104. if (rawNum === null) {
  105. rawNum = 0;
  106. }
  107. var scales = this.getScalingProperties(rawNum, isPercent);
  108. return {
  109. min: scales.scalingOptions[0],
  110. max: scales.scalingOptions[scales.scalingOptions.length - 1]
  111. };
  112. },
  113. /**
  114. * Given a corresponding value and scale to use, calculate the number of shapes and partial shapes to return to the user
  115. * @param value - The value we are calculating the number off of
  116. * @param scale - The scale that will be used
  117. * @param isPercent - If the raw number is used to represent a percentage infographic
  118. * @returns An object that contains the number of shapes and partial shapes to be displayed in the infographic summary
  119. */
  120. getScaleComponents: function getScaleComponents(value, scale, isPercent) {
  121. // If rawNum is null (i.e. no value provided, treat same as zero case to avoid returning errors later on
  122. if (value === null) {
  123. value = 0;
  124. }
  125. /**
  126. * Used to divide a number by a floating point decimal, which can sometimes lead to repeating decimal values using the normal '/' operator
  127. * @param numerator - Numerator of division expression
  128. * @param denominator - Denominator of division expression
  129. * @returns Result of numerator divided by denominator
  130. */
  131. function _division(numerator, denominator) {
  132. while (denominator < 1 && denominator > 0) {
  133. denominator *= 10;
  134. numerator *= 10;
  135. }
  136. return numerator / denominator;
  137. }
  138. /**
  139. * Used to find the remainder of a number by a floating point decimal, which can sometimes lead to repeating decimal values using the normal '%' operator
  140. * @param numerator - Numerator of remainder expression
  141. * @param denominator - Denominator of remainder expression
  142. * @returns remainder of expression
  143. */
  144. function _remainder(numerator, denominator) {
  145. return numerator - denominator * Math.floor(_division(numerator, denominator));
  146. }
  147. value = Math.abs(value);
  148. scale = Math.abs(scale);
  149. var numShapes = Math.floor(_division(value, scale));
  150. var partialValue = _division(_remainder(value, scale), scale);
  151. partialValue = parseFloat(partialValue.toFixed(2));
  152. var numGreyedShapes;
  153. if (isPercent) {
  154. numGreyedShapes = _division(1, scale) - numShapes - Math.ceil(partialValue);
  155. } else {
  156. numGreyedShapes = 0;
  157. }
  158. return {
  159. numShapes: numShapes,
  160. partialValue: partialValue,
  161. numGreyedShapes: numGreyedShapes
  162. };
  163. },
  164. _getScaleOptions: function _getScaleOptions(scaleData) {
  165. var numOfScales = scaleData.availableScales.length;
  166. var result;
  167. if (numOfScales === 3) {
  168. result = [this.SCALE_VALUE_FEW, this.SCALE_VALUE_DEFAULT, this.SCALE_VALUE_MANY];
  169. } else if (numOfScales === 1) {
  170. result = [this.SCALE_VALUE_DEFAULT];
  171. } else if (scaleData.availableScales.indexOf(scaleData.optimalScale) === 0) {
  172. result = [this.SCALE_VALUE_FEW, this.SCALE_VALUE_DEFAULT];
  173. } else if (scaleData.availableScales.indexOf(scaleData.optimalScale) === 1) {
  174. result = [this.SCALE_VALUE_DEFAULT, this.SCALE_VALUE_MANY];
  175. }
  176. return result;
  177. },
  178. getSign: function getSign(x) {
  179. x = +x; // convert to a number
  180. if (isNaN(x)) {
  181. return NaN;
  182. }
  183. return x >= 0 ? 1 : -1;
  184. }
  185. });
  186. return new ScaleUtil();
  187. });
  188. //# sourceMappingURL=ScaleUtil.js.map