helper.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. import { round } from 'echarts/lib/util/number'
  2. import { decodeMetricName } from 'containers/Widget/components/util'
  3. type numType = number | string
  4. let _boundaryCheckingState = true
  5. export function getMetricsMinAndMax(metrics, data, stack) {
  6. const metricsSource = metrics.map((metrics) =>
  7. ['min', 'max'].map((item) => {
  8. return { fn: item, data: metrics }
  9. })
  10. )
  11. return metricsSource.flat().map((item) => {
  12. if (stack) {
  13. return item.data.reduce(
  14. (num, m) =>
  15. num +
  16. Math[item.fn](
  17. ...data.map((d) => d[`${m.agg}(${decodeMetricName(m.name)})`])
  18. ),
  19. 0
  20. )
  21. } else {
  22. return Math[item.fn](
  23. ...item.data.map((m) =>
  24. Math[item.fn](
  25. ...data.map((d) => d[`${m.agg}(${decodeMetricName(m.name)})`])
  26. )
  27. )
  28. )
  29. }
  30. })
  31. }
  32. export function getDoubleYExtendInterval(initExtent, splitNumber) {
  33. let [minExtent, maxExtent] = initExtent
  34. if (minExtent > 0 && maxExtent > 0) {
  35. minExtent = 0
  36. }
  37. if (minExtent < 0 && maxExtent < 0) {
  38. maxExtent = 0
  39. }
  40. if (minExtent === maxExtent) {
  41. if (minExtent !== 0) {
  42. const expandSize = minExtent
  43. maxExtent += expandSize / 2
  44. minExtent -= expandSize / 2
  45. } else {
  46. maxExtent = 1
  47. }
  48. }
  49. let differ = maxExtent - minExtent
  50. if (!isFinite(differ)) {
  51. minExtent = 0
  52. maxExtent = 1
  53. differ = maxExtent - minExtent
  54. }
  55. if (!isFinite(differ)) {
  56. return
  57. }
  58. let interval = differ / splitNumber
  59. if (interval === 0) {
  60. return 0
  61. }
  62. let exponent = Math.floor(Math.log(interval) / Math.LN10)
  63. if (interval / Math.pow(10, exponent) >= 10) {
  64. exponent++
  65. }
  66. const exp10 = Math.pow(10, exponent)
  67. const f = interval / exp10
  68. let nf
  69. if (f < 1.5) {
  70. nf = 1
  71. } else if (f < 2.5) {
  72. nf = 2
  73. } else if (f < 4) {
  74. nf = 3
  75. } else if (f < 7) {
  76. nf = 5
  77. } else {
  78. nf = 10
  79. }
  80. interval = nf * exp10
  81. interval = exponent >= -20 ? +interval.toFixed(exponent < 0 ? -exponent : 0) : interval
  82. minExtent = round(Math.floor(minExtent / interval) * interval)
  83. maxExtent = round(Math.ceil(maxExtent / interval) * interval)
  84. return { minExtent, maxExtent, interval }
  85. }
  86. export function strip(num: numType, precision = 12): number {
  87. return +parseFloat(Number(num).toPrecision(precision))
  88. }
  89. export function digitLength(num: numType): number {
  90. const eSplit = num.toString().split(/[eE]/)
  91. const len = (eSplit[0].split('.')[1] || '').length - +(eSplit[1] || 0)
  92. return len > 0 ? len : 0
  93. }
  94. export function float2Fixed(num: numType): number {
  95. if (num.toString().indexOf('e') === -1) {
  96. return Number(num.toString().replace('.', ''))
  97. }
  98. const dLen = digitLength(num)
  99. return dLen > 0 ? strip(Number(num) * Math.pow(10, dLen)) : Number(num)
  100. }
  101. export function checkBoundary(num: number) {
  102. if (_boundaryCheckingState) {
  103. if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {
  104. console.warn(
  105. `${num} is beyond boundary when transfer to integer, the results may not be accurate`
  106. )
  107. }
  108. }
  109. }
  110. export function times(
  111. num1: numType,
  112. num2: numType,
  113. ...others: numType[]
  114. ): number {
  115. if (others.length > 0) {
  116. return times(times(num1, num2), others[0], ...others.slice(1))
  117. }
  118. const num1Changed = float2Fixed(num1)
  119. const num2Changed = float2Fixed(num2)
  120. const baseNum = digitLength(num1) + digitLength(num2)
  121. const leftValue = num1Changed * num2Changed
  122. checkBoundary(leftValue)
  123. return leftValue / Math.pow(10, baseNum)
  124. }
  125. export function divide(
  126. num1: numType,
  127. num2: numType,
  128. ...others: numType[]
  129. ): number {
  130. if (others.length > 0) {
  131. return divide(divide(num1, num2), others[0], ...others.slice(1))
  132. }
  133. const num1Changed = float2Fixed(num1)
  134. const num2Changed = float2Fixed(num2)
  135. checkBoundary(num1Changed)
  136. checkBoundary(num2Changed)
  137. return times(
  138. strip(num1Changed / num2Changed),
  139. strip(Math.pow(10, digitLength(num2) - digitLength(num1)))
  140. )
  141. }
  142. export function getMetricsExtendMinAndMax(
  143. metrics,
  144. secondaryMetrics,
  145. data,
  146. stack,
  147. splitNumber
  148. ) {
  149. const [leftMin, leftMax, rightMin, rightMax] = getMetricsMinAndMax(
  150. [metrics, secondaryMetrics],
  151. data,
  152. stack
  153. )
  154. const [leftExtentMin, leftExtentMax, leftInterval] = Object.values(
  155. getDoubleYExtendInterval([leftMin, leftMax], splitNumber)
  156. )
  157. const [rightExtentMin, rightExtentMax, rightInterval] = Object.values(
  158. getDoubleYExtendInterval([rightMin, rightMax], splitNumber)
  159. )
  160. let maxCount
  161. let minCount
  162. const [
  163. leftMaxPartCount,
  164. rightMaxPartCount,
  165. leftMinPartCount,
  166. rightMinPartCount
  167. ] = [
  168. divide(leftExtentMax, leftInterval),
  169. divide(rightExtentMax, rightInterval),
  170. divide(leftExtentMin, leftInterval),
  171. divide(rightExtentMin, rightInterval)
  172. ]
  173. if (leftExtentMin > 0 && rightExtentMin > 0) {
  174. maxCount = Math.max(leftMaxPartCount, rightMaxPartCount)
  175. minCount = Math.max(leftMinPartCount, rightMinPartCount)
  176. } else if (leftExtentMax < 0 && rightExtentMax < 0) {
  177. maxCount = Math.min(leftMaxPartCount, rightMaxPartCount)
  178. minCount = Math.min(leftMinPartCount, rightMinPartCount)
  179. } else {
  180. maxCount = Math.max(leftMaxPartCount, rightMaxPartCount)
  181. minCount = Math.min(leftMinPartCount, rightMinPartCount)
  182. }
  183. return {
  184. leftY: [
  185. times(minCount, leftInterval),
  186. times(maxCount, leftInterval),
  187. leftInterval
  188. ],
  189. rightY: [
  190. times(minCount, rightInterval),
  191. times(maxCount, rightInterval),
  192. rightInterval
  193. ]
  194. }
  195. }