@visactor/vutils 1.0.23 → 1.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (436) hide show
  1. package/README.zh-CN.md +12 -0
  2. package/cjs/angle.d.ts +61 -0
  3. package/cjs/angle.js +73 -7
  4. package/cjs/angle.js.map +1 -1
  5. package/cjs/color/Color.d.ts +3 -0
  6. package/cjs/color/Color.js +64 -10
  7. package/cjs/color/Color.js.map +1 -1
  8. package/cjs/color/hexToRgb.d.ts +5 -0
  9. package/cjs/color/hexToRgb.js +5 -0
  10. package/cjs/color/hexToRgb.js.map +1 -1
  11. package/cjs/color/rgbToHsl.js +5 -1
  12. package/cjs/color/rgbToHsl.js.map +1 -1
  13. package/cjs/common/array.d.ts +43 -0
  14. package/cjs/common/array.js +44 -0
  15. package/cjs/common/array.js.map +1 -1
  16. package/cjs/common/bisect.d.ts +21 -0
  17. package/cjs/common/bisect.js +29 -0
  18. package/cjs/common/bisect.js.map +1 -1
  19. package/cjs/common/bootstrap.d.ts +5 -0
  20. package/cjs/common/bootstrap.js +22 -0
  21. package/cjs/common/bootstrap.js.map +1 -0
  22. package/cjs/common/clone.js +6 -0
  23. package/cjs/common/clone.js.map +1 -1
  24. package/cjs/common/cloneDeep.js +4 -1
  25. package/cjs/common/cloneDeep.js.map +1 -1
  26. package/cjs/common/correlation.d.ts +3 -0
  27. package/cjs/common/correlation.js +83 -0
  28. package/cjs/common/correlation.js.map +1 -0
  29. package/cjs/common/debounce.js +22 -4
  30. package/cjs/common/debounce.js.map +1 -1
  31. package/cjs/common/ecdf.d.ts +8 -0
  32. package/cjs/common/ecdf.js +11 -2
  33. package/cjs/common/ecdf.js.map +1 -1
  34. package/cjs/common/extent.d.ts +4 -0
  35. package/cjs/common/extent.js +2 -1
  36. package/cjs/common/extent.js.map +1 -1
  37. package/cjs/common/field.d.ts +6 -0
  38. package/cjs/common/field.js +3 -0
  39. package/cjs/common/field.js.map +1 -1
  40. package/cjs/common/getType.js +1 -2
  41. package/cjs/common/has.d.ts +17 -0
  42. package/cjs/common/has.js +18 -1
  43. package/cjs/common/has.js.map +1 -1
  44. package/cjs/common/index.d.ts +4 -0
  45. package/cjs/common/index.js +34 -6
  46. package/cjs/common/index.js.map +1 -1
  47. package/cjs/common/interpolate.js +16 -3
  48. package/cjs/common/interpolate.js.map +1 -1
  49. package/cjs/common/isBase64.d.ts +6 -0
  50. package/cjs/common/isBase64.js +6 -0
  51. package/cjs/common/isBase64.js.map +1 -1
  52. package/cjs/common/isBoolean.d.ts +13 -0
  53. package/cjs/common/isBoolean.js +13 -1
  54. package/cjs/common/isBoolean.js.map +1 -1
  55. package/cjs/common/isEmpty.js +11 -1
  56. package/cjs/common/isEmpty.js.map +1 -1
  57. package/cjs/common/isEqual.js +19 -7
  58. package/cjs/common/isEqual.js.map +1 -1
  59. package/cjs/common/isFunction.d.ts +25 -0
  60. package/cjs/common/isFunction.js +26 -0
  61. package/cjs/common/isFunction.js.map +1 -1
  62. package/cjs/common/isNumber.d.ts +23 -0
  63. package/cjs/common/isNumber.js +23 -1
  64. package/cjs/common/isNumber.js.map +1 -1
  65. package/cjs/common/isNumeric.d.ts +25 -0
  66. package/cjs/common/isNumeric.js +25 -0
  67. package/cjs/common/isNumeric.js.map +1 -1
  68. package/cjs/common/isObject.d.ts +21 -0
  69. package/cjs/common/isObject.js +23 -2
  70. package/cjs/common/isObject.js.map +1 -1
  71. package/cjs/common/isObjectLike.d.ts +20 -0
  72. package/cjs/common/isObjectLike.js +21 -0
  73. package/cjs/common/isObjectLike.js.map +1 -1
  74. package/cjs/common/isPlainObject.d.ts +25 -0
  75. package/cjs/common/isPlainObject.js +2 -1
  76. package/cjs/common/isPlainObject.js.map +1 -1
  77. package/cjs/common/isShallowEqual.js +1 -0
  78. package/cjs/common/isShallowEqual.js.map +1 -1
  79. package/cjs/common/isString.d.ts +13 -0
  80. package/cjs/common/isString.js +13 -1
  81. package/cjs/common/isString.js.map +1 -1
  82. package/cjs/common/isType.d.ts +6 -0
  83. package/cjs/common/isType.js +6 -0
  84. package/cjs/common/isType.js.map +1 -1
  85. package/cjs/common/isUndefined.js +1 -1
  86. package/cjs/common/isValid.js +1 -1
  87. package/cjs/common/isValidNumber.js.map +1 -1
  88. package/cjs/common/isValidUrl.js.map +1 -1
  89. package/cjs/common/kde.d.ts +28 -0
  90. package/cjs/common/kde.js +29 -5
  91. package/cjs/common/kde.js.map +1 -1
  92. package/cjs/common/mean.d.ts +2 -0
  93. package/cjs/common/mean.js +25 -0
  94. package/cjs/common/mean.js.map +1 -0
  95. package/cjs/common/merge.js +20 -4
  96. package/cjs/common/merge.js.map +1 -1
  97. package/cjs/common/mixin.js +14 -3
  98. package/cjs/common/mixin.js.map +1 -1
  99. package/cjs/common/normalQuantile.d.ts +5 -0
  100. package/cjs/common/normalQuantile.js +21 -0
  101. package/cjs/common/normalQuantile.js.map +1 -0
  102. package/cjs/common/number.d.ts +24 -0
  103. package/cjs/common/number.js +24 -1
  104. package/cjs/common/number.js.map +1 -1
  105. package/cjs/common/pad.d.ts +21 -0
  106. package/cjs/common/pad.js +21 -1
  107. package/cjs/common/pad.js.map +1 -1
  108. package/cjs/common/random.d.ts +3 -0
  109. package/cjs/common/random.js +13 -0
  110. package/cjs/common/random.js.map +1 -1
  111. package/cjs/common/regression-linear.d.ts +4 -0
  112. package/cjs/common/regression-linear.js +19 -12
  113. package/cjs/common/regression-linear.js.map +1 -1
  114. package/cjs/common/regression-logistic.d.ts +6 -0
  115. package/cjs/common/regression-logistic.js +25 -14
  116. package/cjs/common/regression-logistic.js.map +1 -1
  117. package/cjs/common/regression-lowess.d.ts +9 -0
  118. package/cjs/common/regression-lowess.js +64 -26
  119. package/cjs/common/regression-lowess.js.map +1 -1
  120. package/cjs/common/regression-polynomial.js +26 -15
  121. package/cjs/common/regression-polynomial.js.map +1 -1
  122. package/cjs/common/regression-utils.d.ts +13 -1
  123. package/cjs/common/regression-utils.js +27 -12
  124. package/cjs/common/regression-utils.js.map +1 -1
  125. package/cjs/common/throttle.js +3 -0
  126. package/cjs/common/throttle.js.map +1 -1
  127. package/cjs/common/toDate.js +22 -3
  128. package/cjs/common/toDate.js.map +1 -1
  129. package/cjs/common/uuid.d.ts +7 -0
  130. package/cjs/common/uuid.js +15 -1
  131. package/cjs/common/uuid.js.map +1 -1
  132. package/cjs/data-structure/bounds.d.ts +101 -0
  133. package/cjs/data-structure/bounds.js +74 -7
  134. package/cjs/data-structure/bounds.js.map +1 -1
  135. package/cjs/data-structure/hashTable.d.ts +4 -0
  136. package/cjs/data-structure/hashTable.js +11 -2
  137. package/cjs/data-structure/hashTable.js.map +1 -1
  138. package/cjs/data-structure/index.js +6 -2
  139. package/cjs/data-structure/index.js.map +1 -1
  140. package/cjs/data-structure/matrix.d.ts +91 -0
  141. package/cjs/data-structure/matrix.js +52 -4
  142. package/cjs/data-structure/matrix.js.map +1 -1
  143. package/cjs/data-structure/point.d.ts +3 -0
  144. package/cjs/data-structure/point.js +5 -1
  145. package/cjs/data-structure/point.js.map +1 -1
  146. package/cjs/dom.d.ts +36 -0
  147. package/cjs/dom.js +43 -1
  148. package/cjs/dom.js.map +1 -1
  149. package/cjs/fmin/blas1.js +10 -1
  150. package/cjs/fmin/blas1.js.map +1 -1
  151. package/cjs/fmin/conjugate-gradient.js +18 -3
  152. package/cjs/fmin/conjugate-gradient.js.map +1 -1
  153. package/cjs/fmin/linesearch.js +15 -1
  154. package/cjs/fmin/linesearch.js.map +1 -1
  155. package/cjs/fmin/nelder-mead.d.ts +1 -0
  156. package/cjs/fmin/nelder-mead.js +30 -10
  157. package/cjs/fmin/nelder-mead.js.map +1 -1
  158. package/cjs/format/number/formatDecimal.js +9 -1
  159. package/cjs/format/number/formatDecimal.js.map +1 -1
  160. package/cjs/format/number/formatGroup.js +7 -0
  161. package/cjs/format/number/formatGroup.js.map +1 -1
  162. package/cjs/format/number/formatPrefixAuto.js +8 -0
  163. package/cjs/format/number/formatPrefixAuto.js.map +1 -1
  164. package/cjs/format/number/formatRounded.js +7 -0
  165. package/cjs/format/number/formatRounded.js.map +1 -1
  166. package/cjs/format/number/formatTrim.js +7 -0
  167. package/cjs/format/number/formatTrim.js.map +1 -1
  168. package/cjs/format/number/number.js +39 -9
  169. package/cjs/format/number/number.js.map +1 -1
  170. package/cjs/format/time.js +23 -6
  171. package/cjs/format/time.js.map +1 -1
  172. package/cjs/geo/circle-intersection.d.ts +12 -0
  173. package/cjs/geo/circle-intersection.js +54 -9
  174. package/cjs/geo/circle-intersection.js.map +1 -1
  175. package/cjs/geo/constant.js +8 -1
  176. package/cjs/geo/constant.js.map +1 -1
  177. package/cjs/geo/invariant.d.ts +22 -0
  178. package/cjs/geo/invariant.js +24 -1
  179. package/cjs/geo/invariant.js.map +1 -1
  180. package/cjs/graphics/algorithm/aabb.js +7 -3
  181. package/cjs/graphics/algorithm/aabb.js.map +1 -1
  182. package/cjs/graphics/algorithm/intersect.d.ts +48 -0
  183. package/cjs/graphics/algorithm/intersect.js +75 -7
  184. package/cjs/graphics/algorithm/intersect.js.map +1 -1
  185. package/cjs/graphics/bounds-util.js +4 -2
  186. package/cjs/graphics/bounds-util.js.map +1 -1
  187. package/cjs/graphics/graph-util.js +4 -0
  188. package/cjs/graphics/graph-util.js.map +1 -1
  189. package/cjs/graphics/index.js +7 -4
  190. package/cjs/graphics/index.js.map +1 -1
  191. package/cjs/graphics/polygon.d.ts +12 -0
  192. package/cjs/graphics/polygon.js +28 -5
  193. package/cjs/graphics/polygon.js.map +1 -1
  194. package/cjs/graphics/text/measure/interface.d.ts +12 -0
  195. package/cjs/graphics/text/measure/textMeasure.d.ts +23 -0
  196. package/cjs/graphics/text/measure/textMeasure.js +50 -22
  197. package/cjs/graphics/text/measure/textMeasure.js.map +1 -1
  198. package/cjs/graphics/text/measure/util.js +1 -0
  199. package/cjs/graphics/text/measure/util.js.map +1 -1
  200. package/cjs/graphics/text/stringWidth.js +7 -3
  201. package/cjs/graphics/text/stringWidth.js.map +1 -1
  202. package/cjs/index.d.ts +6 -0
  203. package/cjs/index.js +19 -4
  204. package/cjs/index.js.map +1 -1
  205. package/cjs/logger.js +12 -2
  206. package/cjs/logger.js.map +1 -1
  207. package/cjs/lru.js +14 -6
  208. package/cjs/lru.js.map +1 -1
  209. package/cjs/math.d.ts +9 -0
  210. package/cjs/math.js +10 -0
  211. package/cjs/math.js.map +1 -1
  212. package/cjs/padding.d.ts +5 -0
  213. package/cjs/padding.js +5 -0
  214. package/cjs/padding.js.map +1 -1
  215. package/cjs/time/formatUtils.d.ts +6 -0
  216. package/cjs/time/formatUtils.js +7 -0
  217. package/cjs/time/formatUtils.js.map +1 -1
  218. package/dist/index.js +1627 -116
  219. package/dist/index.min.js +128 -1
  220. package/es/angle.d.ts +61 -0
  221. package/es/angle.js +66 -10
  222. package/es/angle.js.map +1 -1
  223. package/es/color/Color.d.ts +3 -0
  224. package/es/color/Color.js +64 -9
  225. package/es/color/Color.js.map +1 -1
  226. package/es/color/hexToRgb.d.ts +5 -0
  227. package/es/color/hexToRgb.js +5 -0
  228. package/es/color/hexToRgb.js.map +1 -1
  229. package/es/color/rgbToHsl.js +5 -1
  230. package/es/color/rgbToHsl.js.map +1 -1
  231. package/es/common/array.d.ts +43 -0
  232. package/es/common/array.js +44 -8
  233. package/es/common/array.js.map +1 -1
  234. package/es/common/bisect.d.ts +21 -0
  235. package/es/common/bisect.js +29 -4
  236. package/es/common/bisect.js.map +1 -1
  237. package/es/common/bootstrap.d.ts +5 -0
  238. package/es/common/bootstrap.js +14 -0
  239. package/es/common/bootstrap.js.map +1 -0
  240. package/es/common/clone.js +6 -0
  241. package/es/common/clone.js.map +1 -1
  242. package/es/common/cloneDeep.js +4 -1
  243. package/es/common/cloneDeep.js.map +1 -1
  244. package/es/common/correlation.d.ts +3 -0
  245. package/es/common/correlation.js +77 -0
  246. package/es/common/correlation.js.map +1 -0
  247. package/es/common/debounce.js +22 -4
  248. package/es/common/debounce.js.map +1 -1
  249. package/es/common/ecdf.d.ts +8 -0
  250. package/es/common/ecdf.js +11 -1
  251. package/es/common/ecdf.js.map +1 -1
  252. package/es/common/extent.d.ts +4 -0
  253. package/es/common/extent.js +6 -2
  254. package/es/common/extent.js.map +1 -1
  255. package/es/common/field.d.ts +6 -0
  256. package/es/common/field.js +3 -1
  257. package/es/common/field.js.map +1 -1
  258. package/es/common/getType.js +1 -2
  259. package/es/common/has.d.ts +17 -0
  260. package/es/common/has.js +18 -1
  261. package/es/common/has.js.map +1 -1
  262. package/es/common/index.d.ts +4 -0
  263. package/es/common/index.js +24 -1
  264. package/es/common/index.js.map +1 -1
  265. package/es/common/interpolate.js +16 -3
  266. package/es/common/interpolate.js.map +1 -1
  267. package/es/common/isBase64.d.ts +6 -0
  268. package/es/common/isBase64.js +6 -0
  269. package/es/common/isBase64.js.map +1 -1
  270. package/es/common/isBoolean.d.ts +13 -0
  271. package/es/common/isBoolean.js +13 -1
  272. package/es/common/isBoolean.js.map +1 -1
  273. package/es/common/isEmpty.js +11 -1
  274. package/es/common/isEmpty.js.map +1 -1
  275. package/es/common/isEqual.js +19 -7
  276. package/es/common/isEqual.js.map +1 -1
  277. package/es/common/isFunction.d.ts +25 -0
  278. package/es/common/isFunction.js +26 -0
  279. package/es/common/isFunction.js.map +1 -1
  280. package/es/common/isNumber.d.ts +23 -0
  281. package/es/common/isNumber.js +23 -1
  282. package/es/common/isNumber.js.map +1 -1
  283. package/es/common/isNumeric.d.ts +25 -0
  284. package/es/common/isNumeric.js +25 -0
  285. package/es/common/isNumeric.js.map +1 -1
  286. package/es/common/isObject.d.ts +21 -0
  287. package/es/common/isObject.js +23 -2
  288. package/es/common/isObject.js.map +1 -1
  289. package/es/common/isObjectLike.d.ts +20 -0
  290. package/es/common/isObjectLike.js +21 -0
  291. package/es/common/isObjectLike.js.map +1 -1
  292. package/es/common/isPlainObject.d.ts +25 -0
  293. package/es/common/isPlainObject.js +28 -1
  294. package/es/common/isPlainObject.js.map +1 -1
  295. package/es/common/isShallowEqual.js +1 -0
  296. package/es/common/isShallowEqual.js.map +1 -1
  297. package/es/common/isString.d.ts +13 -0
  298. package/es/common/isString.js +13 -1
  299. package/es/common/isString.js.map +1 -1
  300. package/es/common/isType.d.ts +6 -0
  301. package/es/common/isType.js +6 -0
  302. package/es/common/isType.js.map +1 -1
  303. package/es/common/isUndefined.js +1 -1
  304. package/es/common/isValid.js +1 -1
  305. package/es/common/isValidNumber.js.map +1 -1
  306. package/es/common/isValidUrl.js.map +1 -1
  307. package/es/common/kde.d.ts +28 -0
  308. package/es/common/kde.js +29 -7
  309. package/es/common/kde.js.map +1 -1
  310. package/es/common/mean.d.ts +2 -0
  311. package/es/common/mean.js +19 -0
  312. package/es/common/mean.js.map +1 -0
  313. package/es/common/merge.js +22 -5
  314. package/es/common/merge.js.map +1 -1
  315. package/es/common/mixin.js +14 -3
  316. package/es/common/mixin.js.map +1 -1
  317. package/es/common/normalQuantile.d.ts +5 -0
  318. package/es/common/normalQuantile.js +15 -0
  319. package/es/common/normalQuantile.js.map +1 -0
  320. package/es/common/number.d.ts +24 -0
  321. package/es/common/number.js +24 -2
  322. package/es/common/number.js.map +1 -1
  323. package/es/common/pad.d.ts +21 -0
  324. package/es/common/pad.js +21 -1
  325. package/es/common/pad.js.map +1 -1
  326. package/es/common/random.d.ts +3 -0
  327. package/es/common/random.js +13 -1
  328. package/es/common/random.js.map +1 -1
  329. package/es/common/regression-linear.d.ts +4 -0
  330. package/es/common/regression-linear.js +18 -14
  331. package/es/common/regression-linear.js.map +1 -1
  332. package/es/common/regression-logistic.d.ts +6 -0
  333. package/es/common/regression-logistic.js +24 -16
  334. package/es/common/regression-logistic.js.map +1 -1
  335. package/es/common/regression-lowess.d.ts +9 -0
  336. package/es/common/regression-lowess.js +63 -27
  337. package/es/common/regression-lowess.js.map +1 -1
  338. package/es/common/regression-polynomial.js +25 -16
  339. package/es/common/regression-polynomial.js.map +1 -1
  340. package/es/common/regression-utils.d.ts +13 -1
  341. package/es/common/regression-utils.js +18 -9
  342. package/es/common/regression-utils.js.map +1 -1
  343. package/es/common/throttle.js +3 -0
  344. package/es/common/throttle.js.map +1 -1
  345. package/es/common/toDate.js +23 -3
  346. package/es/common/toDate.js.map +1 -1
  347. package/es/common/uuid.d.ts +7 -0
  348. package/es/common/uuid.js +15 -1
  349. package/es/common/uuid.js.map +1 -1
  350. package/es/data-structure/bounds.d.ts +101 -0
  351. package/es/data-structure/bounds.js +74 -8
  352. package/es/data-structure/bounds.js.map +1 -1
  353. package/es/data-structure/hashTable.d.ts +4 -0
  354. package/es/data-structure/hashTable.js +11 -2
  355. package/es/data-structure/hashTable.js.map +1 -1
  356. package/es/data-structure/index.js +2 -0
  357. package/es/data-structure/index.js.map +1 -1
  358. package/es/data-structure/matrix.d.ts +91 -0
  359. package/es/data-structure/matrix.js +52 -5
  360. package/es/data-structure/matrix.js.map +1 -1
  361. package/es/data-structure/point.d.ts +3 -0
  362. package/es/data-structure/point.js +5 -1
  363. package/es/data-structure/point.js.map +1 -1
  364. package/es/dom.d.ts +36 -0
  365. package/es/dom.js +43 -8
  366. package/es/dom.js.map +1 -1
  367. package/es/fmin/blas1.js +10 -0
  368. package/es/fmin/blas1.js.map +1 -1
  369. package/es/fmin/conjugate-gradient.js +18 -2
  370. package/es/fmin/conjugate-gradient.js.map +1 -1
  371. package/es/fmin/linesearch.js +15 -0
  372. package/es/fmin/linesearch.js.map +1 -1
  373. package/es/fmin/nelder-mead.d.ts +1 -0
  374. package/es/fmin/nelder-mead.js +29 -9
  375. package/es/fmin/nelder-mead.js.map +1 -1
  376. package/es/format/number/formatDecimal.js +9 -1
  377. package/es/format/number/formatDecimal.js.map +1 -1
  378. package/es/format/number/formatGroup.js +7 -0
  379. package/es/format/number/formatGroup.js.map +1 -1
  380. package/es/format/number/formatPrefixAuto.js +8 -0
  381. package/es/format/number/formatPrefixAuto.js.map +1 -1
  382. package/es/format/number/formatRounded.js +7 -0
  383. package/es/format/number/formatRounded.js.map +1 -1
  384. package/es/format/number/formatTrim.js +7 -0
  385. package/es/format/number/formatTrim.js.map +1 -1
  386. package/es/format/number/number.js +40 -8
  387. package/es/format/number/number.js.map +1 -1
  388. package/es/format/time.js +23 -5
  389. package/es/format/time.js.map +1 -1
  390. package/es/geo/circle-intersection.d.ts +12 -0
  391. package/es/geo/circle-intersection.js +54 -14
  392. package/es/geo/circle-intersection.js.map +1 -1
  393. package/es/geo/constant.js +8 -0
  394. package/es/geo/constant.js.map +1 -1
  395. package/es/geo/invariant.d.ts +22 -0
  396. package/es/geo/invariant.js +24 -0
  397. package/es/geo/invariant.js.map +1 -1
  398. package/es/graphics/algorithm/aabb.js +7 -3
  399. package/es/graphics/algorithm/aabb.js.map +1 -1
  400. package/es/graphics/algorithm/intersect.d.ts +48 -0
  401. package/es/graphics/algorithm/intersect.js +75 -12
  402. package/es/graphics/algorithm/intersect.js.map +1 -1
  403. package/es/graphics/bounds-util.js +4 -2
  404. package/es/graphics/bounds-util.js.map +1 -1
  405. package/es/graphics/graph-util.js +4 -0
  406. package/es/graphics/graph-util.js.map +1 -1
  407. package/es/graphics/index.js +2 -0
  408. package/es/graphics/index.js.map +1 -1
  409. package/es/graphics/polygon.d.ts +12 -0
  410. package/es/graphics/polygon.js +28 -6
  411. package/es/graphics/polygon.js.map +1 -1
  412. package/es/graphics/text/measure/interface.d.ts +12 -0
  413. package/es/graphics/text/measure/textMeasure.d.ts +23 -0
  414. package/es/graphics/text/measure/textMeasure.js +46 -21
  415. package/es/graphics/text/measure/textMeasure.js.map +1 -1
  416. package/es/graphics/text/measure/util.js +1 -0
  417. package/es/graphics/text/measure/util.js.map +1 -1
  418. package/es/graphics/text/stringWidth.js +7 -3
  419. package/es/graphics/text/stringWidth.js.map +1 -1
  420. package/es/index.d.ts +6 -0
  421. package/es/index.js +15 -1
  422. package/es/index.js.map +1 -1
  423. package/es/logger.js +12 -1
  424. package/es/logger.js.map +1 -1
  425. package/es/lru.js +14 -6
  426. package/es/lru.js.map +1 -1
  427. package/es/math.d.ts +9 -0
  428. package/es/math.js +10 -1
  429. package/es/math.js.map +1 -1
  430. package/es/padding.d.ts +5 -0
  431. package/es/padding.js +5 -1
  432. package/es/padding.js.map +1 -1
  433. package/es/time/formatUtils.d.ts +6 -0
  434. package/es/time/formatUtils.js +7 -1
  435. package/es/time/formatUtils.js.map +1 -1
  436. package/package.json +8 -3
package/dist/index.js CHANGED
@@ -325,8 +325,27 @@
325
325
  var eventemitter3Exports = eventemitter3.exports;
326
326
  var index$1 = /*@__PURE__*/getDefaultExportFromCjs(eventemitter3Exports);
327
327
 
328
+ /**
329
+ * check value type
330
+ * @param value the value to check
331
+ * @param type type
332
+ * @returns
333
+ */
328
334
  const isType = (value, type) => Object.prototype.toString.call(value) === `[object ${type}]`;
329
335
 
336
+ /**
337
+ * Checks if `value` is classified as a boolean primitive or object.
338
+ * @param {*} value The value to check.
339
+ * @param {boolean} fuzzy Whether to perform fuzzy judgment, for better performance,default to `false`.
340
+ * @returns {boolean} Returns `true` if `value` is a boolean, else `false`.
341
+ * @example
342
+ *
343
+ * isBoolean(false)
344
+ * // => true
345
+ *
346
+ * isBoolean(null)
347
+ * // => false
348
+ */
330
349
  const isBoolean = (value, fuzzy = false) => {
331
350
  if (fuzzy) {
332
351
  return typeof value === 'boolean';
@@ -334,6 +353,32 @@
334
353
  return value === true || value === false || isType(value, 'Boolean');
335
354
  };
336
355
 
356
+ /**
357
+ * Checks if `value` is classified as a `Function` object.
358
+ *
359
+ * @param {*} value The value to check.
360
+ * @returns {boolean} Returns `true` if `value` is a function, else `false`.
361
+ * @example
362
+ *
363
+ * isFunction(class Any{})
364
+ * // => true
365
+ *
366
+ * isFunction(() => {})
367
+ * // => true
368
+ *
369
+ * isFunction(async () => {})
370
+ * // => true
371
+ *
372
+ * isFunction(function * Any() {})
373
+ * // => true
374
+ *
375
+ * isFunction(Math.round)
376
+ * // => true
377
+ *
378
+ * isFunction(/abc/)
379
+ * // => false
380
+ */
381
+ // eslint-disable-next-line @typescript-eslint/ban-types
337
382
  const isFunction = (value) => {
338
383
  return typeof value === 'function';
339
384
  };
@@ -350,15 +395,84 @@
350
395
  return value !== null && value !== undefined;
351
396
  };
352
397
 
398
+ /**
399
+ * Checks if `value` is the
400
+ * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
401
+ * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
402
+ *
403
+ * @param {*} value The value to check.
404
+ * @returns {boolean} Returns `true` if `value` is an object, else `false`.
405
+ * @example
406
+ *
407
+ * isObject({})
408
+ * // => true
409
+ *
410
+ * isObject([1, 2, 3])
411
+ * // => true
412
+ *
413
+ * isObject(Function)
414
+ * // => true
415
+ *
416
+ * isObject(null)
417
+ * // => false
418
+ */
419
+ // eslint-disable-next-line @typescript-eslint/ban-types
353
420
  const isObject = (value) => {
354
421
  const type = typeof value;
355
422
  return (value !== null && type === 'object') || type === 'function';
356
423
  };
357
424
 
425
+ /**
426
+ * Checks if `value` is object-like. A value is object-like if it's not `null`
427
+ * and has a `typeof` result of "object".
428
+ *
429
+ * @param {*} value The value to check.
430
+ * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
431
+ * @example
432
+ *
433
+ * isObjectLike({})
434
+ * // => true
435
+ *
436
+ * isObjectLike([1, 2, 3])
437
+ * // => true
438
+ *
439
+ * isObjectLike(Function)
440
+ * // => false
441
+ *
442
+ * isObjectLike(null)
443
+ * // => false
444
+ */
445
+ // eslint-disable-next-line @typescript-eslint/ban-types
358
446
  const isObjectLike = (value) => {
359
447
  return typeof value === 'object' && value !== null;
360
448
  };
361
449
 
450
+ /**
451
+ * @see https://github.com/lodash/lodash/blob/master/isPlainObject.js
452
+ * Checks if `value` is a plain object, that is, an object created by the
453
+ * `Object` constructor or one with a `[[Prototype]]` of `null`.
454
+ *
455
+ * @param {*} value The value to check.
456
+ * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
457
+ * @example
458
+ *
459
+ * function Foo() {
460
+ * this.a = 1
461
+ * }
462
+ *
463
+ * isPlainObject(new Foo)
464
+ * // => false
465
+ *
466
+ * isPlainObject([1, 2, 3])
467
+ * // => false
468
+ *
469
+ * isPlainObject({ 'x': 0, 'y': 0 })
470
+ * // => true
471
+ *
472
+ * isPlainObject(Object.create(null))
473
+ * // => true
474
+ */
475
+ // eslint-disable-next-line @typescript-eslint/ban-types
362
476
  const isPlainObject = function (value) {
363
477
  if (!isObjectLike(value) || !isType(value, 'Object')) {
364
478
  return false;
@@ -377,6 +491,19 @@
377
491
  return value === undefined;
378
492
  };
379
493
 
494
+ /**
495
+ * Checks if `value` is classified as a `String` primitive or object.
496
+ * @param {*} value The value to check.
497
+ * @param {boolean} fuzzy Whether to perform fuzzy judgment, for better performance,default to `false`.
498
+ * @returns {boolean} Returns `true` if `value` is a string, else `false`.
499
+ * @example
500
+ *
501
+ * isString('abc')
502
+ * // => true
503
+ *
504
+ * isString(1)
505
+ * // => false
506
+ */
380
507
  const isString = (value, fuzzy = false) => {
381
508
  const type = typeof value;
382
509
  if (fuzzy) {
@@ -397,6 +524,29 @@
397
524
  return isType(value, 'Date');
398
525
  };
399
526
 
527
+ /**
528
+ * Checks if `value` is classified as a `Number` primitive or object.
529
+ *
530
+ * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are
531
+ * classified as numbers, use the `Number.isFinite` method.
532
+ *
533
+ * @param {*} value The value to check.
534
+ * @param {boolean} fuzzy Whether to perform fuzzy judgment, for better performance,default to `false`.
535
+ * @returns {boolean} Returns `true` if `value` is a number, else `false`.
536
+ * @example
537
+ *
538
+ * isNumber(3)
539
+ * // => true
540
+ *
541
+ * isNumber(Number.MIN_VALUE)
542
+ * // => true
543
+ *
544
+ * isNumber(Infinity)
545
+ * // => true
546
+ *
547
+ * isNumber('3')
548
+ * // => false
549
+ */
400
550
  const isNumber = (value, fuzzy = false) => {
401
551
  const type = typeof value;
402
552
  if (fuzzy) {
@@ -405,6 +555,31 @@
405
555
  return type === 'number' || isType(value, 'Number');
406
556
  };
407
557
 
558
+ /**
559
+ * Checks if `value` is classified as a legal string number
560
+ *
561
+ * @param {*} value The string value to check.
562
+ * @returns {boolean} Returns `true` if `value` is a legal string number, else `false`.
563
+ * @example
564
+ *
565
+ * isNumeric(1)
566
+ * // => false
567
+ *
568
+ * isNumeric('2.0')
569
+ * // => true
570
+ *
571
+ * isNumeric('3a')
572
+ * // => false
573
+ *
574
+ * isNumeric('4.a')
575
+ * // => false
576
+ *
577
+ * isNumeric(Infinity)
578
+ * // => false
579
+ *
580
+ * isNumeric('01')
581
+ * // => true
582
+ */
408
583
  const isNumeric = (value) => {
409
584
  if (typeof value !== 'string') {
410
585
  return false;
@@ -413,10 +588,12 @@
413
588
  };
414
589
 
415
590
  const isValidNumber = (value) => {
591
+ // isFinate 包含来 isNaN 的判断
416
592
  return isNumber(value) && Number.isFinite(value);
417
593
  };
418
594
 
419
595
  const isValidUrl = (value) => {
596
+ // TODO: 匹配其他形式的 url
420
597
  const exp = /^(http(s)?:\/\/)\w+[^\s]+(\.[^\s]+){1,}$/;
421
598
  const urlPattern = new RegExp(exp);
422
599
  return urlPattern.test(value);
@@ -426,6 +603,12 @@
426
603
  return isType(value, 'RegExp');
427
604
  };
428
605
 
606
+ /**
607
+ * 迁移至 sirius
608
+ * Checks if `value` is classified as a `Base64` string;
609
+ * @param {string} The value to check.
610
+ * @returns {boolean} Returns `true` if `value` is a boolean, else `false`.
611
+ */
429
612
  const isBase64 = (value) => {
430
613
  const exp = /^data:image\/(?:gif|png|jpeg|bmp|webp|svg\+xml)(?:;charset=utf-8)?;base64,(?:[A-Za-z0-9]|[+/])+={0,2}/g;
431
614
  const urlPattern = new RegExp(exp);
@@ -448,12 +631,22 @@
448
631
 
449
632
  const hasOwnProperty$2 = Object.prototype.hasOwnProperty;
450
633
  function isEmpty(value) {
634
+ /**
635
+ * isEmpty(null) => true
636
+ * isEmpty() => true
637
+ * isEmpty(true) => true
638
+ * isEmpty(1) => true
639
+ * isEmpty([1, 2, 3]) => false
640
+ * isEmpty('abc') => false
641
+ * isEmpty({ a: 1 }) => false
642
+ */
451
643
  if (isNil(value)) {
452
644
  return true;
453
645
  }
454
646
  if (isArrayLike(value)) {
455
647
  return !value.length;
456
648
  }
649
+ // TODO: 这里需要优化下
457
650
  const type = getType(value);
458
651
  if (type === 'Map' || type === 'Set') {
459
652
  return !value.size;
@@ -477,7 +670,25 @@
477
670
  return obj === undefined ? defaultValue : obj;
478
671
  };
479
672
 
673
+ /** Used to check objects for own properties. */
480
674
  const hasOwnProperty$1 = Object.prototype.hasOwnProperty;
675
+ /**
676
+ * Checks if `key` is a direct property of `object`.
677
+ *
678
+ * @param {Object} object The object to query.
679
+ * @param {string} key The key to check.
680
+ * @returns {boolean} Returns `true` if `key` exists, else `false`.
681
+ * @example
682
+ *
683
+ * const object = { 'a': { 'b': 2 } }
684
+ * const other = create({ 'a': create({ 'b': 2 }) })
685
+ *
686
+ * has(object, 'a')
687
+ * // => true
688
+ *
689
+ * has(other, 'a')
690
+ * // => false
691
+ */
481
692
  const has = (object, key) => {
482
693
  return object != null && hasOwnProperty$1.call(object, key);
483
694
  };
@@ -489,7 +700,11 @@
489
700
  re.multiline && (flags += 'm');
490
701
  return flags;
491
702
  }
703
+ // Adapted from https://github.com/pvorb/clone by Paul Vorbach
704
+ // License: https://github.com/pvorb/clone/blob/master/LICENSE
492
705
  function clone(parent, circular = false, depth = 0, prototype = undefined) {
706
+ // maintain two arrays for circular references, where corresponding parents
707
+ // and children have the same index
493
708
  const allParents = [];
494
709
  const allChildren = [];
495
710
  if (typeof circular === 'undefined') {
@@ -498,7 +713,9 @@
498
713
  if (typeof depth === 'undefined') {
499
714
  depth = Infinity;
500
715
  }
716
+ // recurse this function so we don't reset allParents and allChildren
501
717
  function _clone(parent, depth) {
718
+ // cloning null always returns null
502
719
  if (parent === null) {
503
720
  return null;
504
721
  }
@@ -552,21 +769,27 @@
552
769
  }
553
770
  const isArr = isArray(value);
554
771
  const length = value.length;
772
+ // 不考虑特殊数组的额外处理
555
773
  if (isArr) {
556
774
  result = new Array(length);
557
775
  }
776
+ // 不考虑 buffer / arguments 类型的处理以及 prototype 的额外处理
558
777
  else if (typeof value === 'object') {
559
778
  result = {};
560
779
  }
780
+ // 不建议使用作为 Boolean / Number / String 作为构造器
561
781
  else if (isBoolean(value) || isNumber(value) || isString(value)) {
562
782
  result = value;
563
783
  }
564
784
  else if (isDate(value)) {
565
785
  result = new Date(+value);
566
786
  }
787
+ // 不考虑 ArrayBuffer / DataView / TypedArray / map / set / regexp / symbol 类型
567
788
  else {
568
789
  result = undefined;
569
790
  }
791
+ // 不考虑 map / set / TypedArray 类型的赋值
792
+ // 不考虑对象的 symbol 属性
570
793
  const props = isArr ? undefined : Object.keys(Object(value));
571
794
  let index = -1;
572
795
  if (result) {
@@ -590,8 +813,10 @@
590
813
  return;
591
814
  }
592
815
  if (isValid(source) && typeof source === 'object') {
816
+ // baseFor
593
817
  const iterable = Object(source);
594
818
  const props = [];
819
+ // keysIn
595
820
  for (const key in iterable) {
596
821
  props.push(key);
597
822
  }
@@ -599,6 +824,7 @@
599
824
  let propIndex = -1;
600
825
  while (length--) {
601
826
  const key = props[++propIndex];
827
+ // skipArray 这个是vchart spec的特有逻辑
602
828
  if (isValid(iterable[key]) &&
603
829
  typeof iterable[key] === 'object' &&
604
830
  (!skipTargetArray || !isArray(target[key]))) {
@@ -611,19 +837,24 @@
611
837
  }
612
838
  }
613
839
  }
840
+ // 由于目前 ChartSpace 内部对 spec 会先执行一次深拷贝,merge 暂时不考虑 source 中有环的问题
841
+ // eslint-disable-next-line @typescript-eslint/ban-types
614
842
  function baseMergeDeep(target, source, key, shallowArray = false, skipTargetArray = false) {
615
843
  const objValue = target[key];
616
844
  const srcValue = source[key];
617
845
  let newValue = source[key];
618
846
  let isCommon = true;
847
+ // 不考虑 buffer / typedArray 类型
619
848
  if (isArray(srcValue)) {
620
849
  if (shallowArray) {
850
+ // 依据参数对数组做浅拷贝
621
851
  newValue = [];
622
852
  }
623
853
  else if (isArray(objValue)) {
624
854
  newValue = objValue;
625
855
  }
626
856
  else if (isArrayLike(objValue)) {
857
+ // 如果 source 为数组,则 target 的 arrayLike 对象也视作为数组处理
627
858
  newValue = new Array(objValue.length);
628
859
  let index = -1;
629
860
  const length = objValue.length;
@@ -632,8 +863,13 @@
632
863
  }
633
864
  }
634
865
  }
866
+ // else if (isArray(srcValue) && shallowArray) {
867
+ // newValue = [];
868
+ // }
869
+ // 不考虑 argument 类型
635
870
  else if (isPlainObject(srcValue)) {
636
871
  newValue = objValue !== null && objValue !== void 0 ? objValue : {};
872
+ // 不考虑 prototype 的额外处理
637
873
  if (typeof objValue === 'function' || typeof objValue !== 'object') {
638
874
  newValue = {};
639
875
  }
@@ -641,19 +877,24 @@
641
877
  else {
642
878
  isCommon = false;
643
879
  }
880
+ // 对 class 等复杂对象或者浅拷贝的 array 不做拷贝处理
644
881
  if (isCommon) {
645
882
  baseMerge(newValue, srcValue, shallowArray, skipTargetArray);
646
883
  }
647
884
  assignMergeValue(target, key, newValue);
648
885
  }
886
+ // eslint-disable-next-line @typescript-eslint/ban-types
649
887
  function assignMergeValue(target, key, value) {
650
888
  if ((value !== undefined && !eq(target[key], value)) || (value === undefined && !(key in target))) {
889
+ // 不考虑 __proto__ 的赋值处理
651
890
  target[key] = value;
652
891
  }
653
892
  }
654
893
  function eq(value, other) {
655
894
  return value === other || (Number.isNaN(value) && Number.isNaN(other));
656
895
  }
896
+ // 与原生的 lodash merge 差异在于对数组是否应用最后一个 source 的结果
897
+ // 例如 lineDash 等 spec 的 merge 使用这一操作更合适
657
898
  function merge(target, ...sources) {
658
899
  let sourceIndex = -1;
659
900
  const length = sources.length;
@@ -707,6 +948,8 @@
707
948
  function objectKeys(obj) {
708
949
  return Object.keys(obj);
709
950
  }
951
+ // Adapted from https://github.com/antvis/F2/blob/master/packages/f2/src/base/equal.ts by zengyue
952
+ // License: https://github.com/antvis/F2/blob/master/packages/f2/LICENSE
710
953
  function isEqual(a, b, options) {
711
954
  if (a === b) {
712
955
  return true;
@@ -714,18 +957,22 @@
714
957
  if (typeof a !== typeof b) {
715
958
  return false;
716
959
  }
960
+ // null 和 undefined
717
961
  if (a == null || b == null) {
718
962
  return false;
719
963
  }
964
+ // 特殊处理NaN
720
965
  if (Number.isNaN(a) && Number.isNaN(b)) {
721
966
  return true;
722
967
  }
723
968
  if (objToString(a) !== objToString(b)) {
724
969
  return false;
725
970
  }
971
+ // 如果是function,则不相等
726
972
  if (isFunction(a)) {
727
973
  return !!(options === null || options === void 0 ? void 0 : options.skipFunction);
728
974
  }
975
+ // 值类型,Number String Boolean
729
976
  if (typeof a !== 'object') {
730
977
  return false;
731
978
  }
@@ -745,16 +992,21 @@
745
992
  }
746
993
  const ka = objectKeys(a);
747
994
  const kb = objectKeys(b);
995
+ // having the same number of owned properties (keys incorporates hasOwnProperty)
748
996
  if (ka.length !== kb.length) {
749
997
  return false;
750
998
  }
999
+ // the same set of keys (although not necessarily the same order),
751
1000
  ka.sort();
752
1001
  kb.sort();
1002
+ // ~~~cheap key test
753
1003
  for (let i = ka.length - 1; i >= 0; i--) {
1004
+ // eslint-disable-next-line eqeqeq
754
1005
  if (ka[i] != kb[i]) {
755
1006
  return false;
756
1007
  }
757
1008
  }
1009
+ // equivalent values for every corresponding key, and ~~~possibly expensive deep test
758
1010
  for (let i = ka.length - 1; i >= 0; i--) {
759
1011
  const key = ka[i];
760
1012
  if (!isEqual(a[key], b[key], options)) {
@@ -768,7 +1020,8 @@
768
1020
  if (x === y) {
769
1021
  return x !== 0 || y !== 0 || 1 / x === 1 / y;
770
1022
  }
771
- return x !== x && y !== y;
1023
+ // eslint-disable-next-line no-self-compare
1024
+ return x !== x && y !== y; // NaN == NaN
772
1025
  }
773
1026
  function length(obj) {
774
1027
  if (isArray(obj)) {
@@ -804,6 +1057,7 @@
804
1057
  return ret;
805
1058
  }
806
1059
 
1060
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint
807
1061
  function keys(obj) {
808
1062
  if (!obj) {
809
1063
  return [];
@@ -813,6 +1067,7 @@
813
1067
  }
814
1068
  const keyList = [];
815
1069
  for (const key in obj) {
1070
+ // eslint-disable-next-line no-prototype-builtins
816
1071
  if (obj.hasOwnProperty(key)) {
817
1072
  keyList.push(key);
818
1073
  }
@@ -829,9 +1084,16 @@
829
1084
  }
830
1085
  return target;
831
1086
  }
1087
+ // eslint-disable-next-line @typescript-eslint/ban-types
832
1088
  function mixin(target, source, override = true) {
1089
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
1090
+ // @ts-ignore
833
1091
  target = 'prototype' in target ? target.prototype : target;
1092
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
1093
+ // @ts-ignore
834
1094
  source = 'prototype' in source ? source.prototype : source;
1095
+ // If build target is ES6 class. prototype methods is not enumerable. Use getOwnPropertyNames instead
1096
+ // TODO: Determine if source is ES6 class?
835
1097
  if (Object.getOwnPropertyNames) {
836
1098
  const keyList = Object.getOwnPropertyNames(source);
837
1099
  for (let i = 0; i < keyList.length; i++) {
@@ -848,12 +1110,22 @@
848
1110
  }
849
1111
  }
850
1112
 
1113
+ /**
1114
+ * 将传入数据转换为数组格式
1115
+ * @param arr 入参。
1116
+ * @returns 数组
1117
+ */
851
1118
  function array(arr) {
852
1119
  if (isValid(arr)) {
853
1120
  return isArray(arr) ? arr : [arr];
854
1121
  }
855
1122
  return [];
856
1123
  }
1124
+ /**
1125
+ * 获取数组的最后一个值,如果传入的参数为非数组,则返回 undefined
1126
+ * @param val any
1127
+ * @returns
1128
+ */
857
1129
  function last(val) {
858
1130
  if (isArrayLike(val)) {
859
1131
  const arr = val;
@@ -861,12 +1133,23 @@
861
1133
  }
862
1134
  return undefined;
863
1135
  }
1136
+ /**
1137
+ * 获取数组第一项与最后一项的差值,如果数组为空则返回 0
1138
+ * @param arr
1139
+ * @returns
1140
+ */
864
1141
  const span = (arr) => {
865
1142
  if (arr.length <= 1) {
866
1143
  return 0;
867
1144
  }
868
1145
  return last(arr) - arr[0];
869
1146
  };
1147
+ /**
1148
+ * 获取数组中的最大值
1149
+ * @param arr 数组
1150
+ * @param compareFn 比较函数,返回正数表示 a > b,返回负数表示 a < b,返回 0 表示相等
1151
+ * @returns
1152
+ */
870
1153
  const maxInArray = (arr, compareFn) => {
871
1154
  var _a;
872
1155
  if (arr.length === 0) {
@@ -881,6 +1164,12 @@
881
1164
  }
882
1165
  return max;
883
1166
  };
1167
+ /**
1168
+ * 获取数组中的最小值
1169
+ * @param arr 数组
1170
+ * @param compareFn 比较函数,返回正数表示 a > b,返回负数表示 a < b,返回 0 表示相等
1171
+ * @returns
1172
+ */
884
1173
  const minInArray = (arr, compareFn) => {
885
1174
  var _a;
886
1175
  if (arr.length === 0) {
@@ -895,6 +1184,12 @@
895
1184
  }
896
1185
  return min;
897
1186
  };
1187
+ /**
1188
+ * 判断两个数组是否相同
1189
+ * @param a
1190
+ * @param b
1191
+ * @returns
1192
+ */
898
1193
  function arrayEqual(a, b) {
899
1194
  if (!isArray(a) || !isArray(b)) {
900
1195
  return false;
@@ -909,12 +1204,18 @@
909
1204
  }
910
1205
  return true;
911
1206
  }
1207
+ /**
1208
+ * 封装并返回无重复的数组
1209
+ * @param arr
1210
+ * @returns
1211
+ */
912
1212
  function uniqArray(arr) {
913
1213
  if (!arr || !isArray(arr)) {
914
1214
  return arr;
915
1215
  }
916
1216
  return Array.from(new Set(array(arr)));
917
1217
  }
1218
+ // Based on http://jsfromhell.com/array/shuffle
918
1219
  function shuffleArray(arr, random = Math.random) {
919
1220
  let j;
920
1221
  let x;
@@ -927,6 +1228,11 @@
927
1228
  }
928
1229
  return arr;
929
1230
  }
1231
+ /**
1232
+ * 展开多层数组,非数组内容将变为 [input]
1233
+ * @param arr
1234
+ * @returns
1235
+ */
930
1236
  function flattenArray(arr) {
931
1237
  if (!isArray(arr)) {
932
1238
  return [arr];
@@ -981,11 +1287,74 @@
981
1287
  return value0 + (value1 - value0) * (i - i0);
982
1288
  }
983
1289
 
1290
+ /**
1291
+ * Approximate inverse normal CDF (probit) using Acklam's algorithm.
1292
+ * Returns z such that P(Z <= z) = p for standard normal Z.
1293
+ */
1294
+ function normalQuantile(p) {
1295
+ if (p === 0) {
1296
+ return -Infinity;
1297
+ }
1298
+ if (p === 1) {
1299
+ return Infinity;
1300
+ }
1301
+ if (!Number.isFinite(p) || p < 0 || p > 1) {
1302
+ return NaN;
1303
+ }
1304
+ const a1 = -39.6968302866538;
1305
+ const a2 = 220.946098424521;
1306
+ const a3 = -275.928510446969;
1307
+ const a4 = 138.357751867269;
1308
+ const a5 = -30.6647980661472;
1309
+ const a6 = 2.50662827745924;
1310
+ const b1 = -54.4760987982241;
1311
+ const b2 = 161.585836858041;
1312
+ const b3 = -155.698979859887;
1313
+ const b4 = 66.8013118877197;
1314
+ const b5 = -13.2806815528857;
1315
+ const c1 = -0.00778489400243029;
1316
+ const c2 = -0.322396458041136;
1317
+ const c3 = -2.40075827716184;
1318
+ const c4 = -2.54973253934373;
1319
+ const c5 = 4.37466414146497;
1320
+ const c6 = 2.93816398269878;
1321
+ const d1 = 0.00778469570904146;
1322
+ const d2 = 0.32246712907004;
1323
+ const d3 = 2.445134137143;
1324
+ const d4 = 3.75440866190742;
1325
+ const pLow = 0.02425;
1326
+ const pHigh = 1 - pLow;
1327
+ let q;
1328
+ let r;
1329
+ if (p < pLow) {
1330
+ q = Math.sqrt(-2 * Math.log(p));
1331
+ return (((((c1 * q + c2) * q + c3) * q + c4) * q + c5) * q + c6) / ((((d1 * q + d2) * q + d3) * q + d4) * q + 1);
1332
+ }
1333
+ if (p <= pHigh) {
1334
+ q = p - 0.5;
1335
+ r = q * q;
1336
+ return (((((((a1 * r + a2) * r + a3) * r + a4) * r + a5) * r + a6) * q) /
1337
+ (((((b1 * r + b2) * r + b3) * r + b4) * r + b5) * r + 1));
1338
+ }
1339
+ q = Math.sqrt(-2 * Math.log(1 - p));
1340
+ return -((((((c1 * q + c2) * q + c3) * q + c4) * q + c5) * q + c6) / ((((d1 * q + d2) * q + d3) * q + d4) * q + 1));
1341
+ }
1342
+
1343
+ /* Adapted from vega by University of Washington Interactive Data Lab
1344
+ * https://vega.github.io/vega/
1345
+ * Licensed under the BSD-3-Clause
1346
+
1347
+ * url: https://github.com/vega/vega/blob/main/packages/vega-util/src/logger.js
1348
+ * License: https://github.com/vega/vega/blob/main/LICENSE
1349
+ * @license
1350
+ */
1351
+ // check console
984
1352
  const hasConsole = typeof console !== 'undefined';
985
1353
  function log(method, level, input) {
986
1354
  const args = [level].concat([].slice.call(input));
987
1355
  if (hasConsole) {
988
- console[method].apply(console, args);
1356
+ // eslint-disable-next-line prefer-spread, no-undef
1357
+ console[method].apply(console, args); // eslint-disable-line no-console
989
1358
  }
990
1359
  }
991
1360
  exports.LoggerLevel = void 0;
@@ -1093,6 +1462,14 @@
1093
1462
  }
1094
1463
  Logger._instance = null;
1095
1464
 
1465
+ /**
1466
+ * 通过二分法,查找数组a中大于数值x的第一个元素的序号
1467
+ * @param a
1468
+ * @param x
1469
+ * @param lo
1470
+ * @param hi
1471
+ * @returns
1472
+ */
1096
1473
  function bisect(a, x, lo = 0, hi) {
1097
1474
  if (isNil(hi)) {
1098
1475
  hi = a.length;
@@ -1108,6 +1485,16 @@
1108
1485
  }
1109
1486
  return lo;
1110
1487
  }
1488
+ /* Adapted from fmin by Ben Frederickson
1489
+ * https://github.com/benfred/fmin
1490
+ * Licensed under the BSD-3-Clause
1491
+
1492
+ * url: https://github.com/benfred/fmin/blob/master/src/bisect.js
1493
+ * License: https://github.com/benfred/fmin/blob/master/LICENSE
1494
+ * @license
1495
+ */
1496
+ /** finds the zeros of a function, given two starting points (which must
1497
+ * have opposite signs */
1111
1498
  function findZeroOfFunction(f, a, b, parameters) {
1112
1499
  var _a, _b;
1113
1500
  const maxIterations = (_a = parameters === null || parameters === void 0 ? void 0 : parameters.maxIterations) !== null && _a !== void 0 ? _a : 100;
@@ -1139,16 +1526,27 @@
1139
1526
  }
1140
1527
  return a + delta;
1141
1528
  }
1529
+ /**
1530
+ * 二分靠近框架,返回数组中第一个大于等于目标值的数的索引
1531
+ * @param arr 数组
1532
+ * @param compareFn 比较函数,返回(当前值-目标值)
1533
+ */
1142
1534
  const binaryFuzzySearch = (arr, compareFn) => {
1143
1535
  return binaryFuzzySearchInNumberRange(0, arr.length, value => compareFn(arr[value]));
1144
1536
  };
1537
+ /**
1538
+ * 二分靠近框架,返回数字区间中第一个大于等于目标值的数字
1539
+ * @param x1 区间上界
1540
+ * @param x2 区间下界(不包含)
1541
+ * @param compareFn 比较函数,返回(当前值-目标值)
1542
+ */
1145
1543
  const binaryFuzzySearchInNumberRange = (x1, x2, compareFn) => {
1146
1544
  let left = x1;
1147
1545
  let right = x2;
1148
1546
  while (left < right) {
1149
1547
  const mid = Math.floor((left + right) / 2);
1150
1548
  if (compareFn(mid) >= 0) {
1151
- right = mid;
1549
+ right = mid; // 第一个大于等于目标值的数
1152
1550
  }
1153
1551
  else {
1154
1552
  left = mid + 1;
@@ -1219,16 +1617,40 @@
1219
1617
  return stop < start ? -step1 : step1;
1220
1618
  }
1221
1619
 
1620
+ /**
1621
+ * @description 处理数值相关的方法
1622
+ */
1222
1623
  const DEFAULT_ABSOLUTE_TOLERATE = 1e-10;
1223
1624
  const DEFAULT_RELATIVE_TOLERATE = 1e-10;
1625
+ /**
1626
+ * 判断两数是否接近相等,函数参数参照 python isClose 方法
1627
+ * @param {number} a
1628
+ * @param {number} b
1629
+ * @param refTol 指定的相对容差比例,将乘以两数的最大值作为相对容差
1630
+ * @param absTol 指定的绝对容差
1631
+ */
1224
1632
  function isNumberClose(a, b, relTol = DEFAULT_RELATIVE_TOLERATE, absTol = DEFAULT_ABSOLUTE_TOLERATE) {
1225
1633
  const abs = absTol;
1226
1634
  const rel = relTol * Math.max(a, b);
1227
1635
  return Math.abs(a - b) <= Math.max(abs, rel);
1228
1636
  }
1637
+ /**
1638
+ * 判断 a 是否大于 b,并排除容差范围
1639
+ * @param a
1640
+ * @param b
1641
+ * @param relTol 指定的相对容差比例,将乘以两数的最大值作为相对容差
1642
+ * @param absTol 指定的绝对容差
1643
+ */
1229
1644
  function isGreater(a, b, relTol, absTol) {
1230
1645
  return a > b && !isNumberClose(a, b, relTol, absTol);
1231
1646
  }
1647
+ /**
1648
+ * 判断 a 是否小于 b,并排除容差范围
1649
+ * @param a
1650
+ * @param b
1651
+ * @param relTol 指定的相对容差比例,将乘以两数的最大值作为相对容差
1652
+ * @param absTol 指定的绝对容差
1653
+ */
1232
1654
  function isLess(a, b, relTol, absTol) {
1233
1655
  return a < b && !isNumberClose(a, b, relTol, absTol);
1234
1656
  }
@@ -1263,6 +1685,27 @@
1263
1685
  }
1264
1686
  return s;
1265
1687
  };
1688
+ /**
1689
+ * Pads `string` on the left and right, left or right sides if it's shorter than `length`.
1690
+ * Padding characters are truncated if they can't be evenly divided by `length`.
1691
+ *
1692
+ * @since 3.0.0
1693
+ * @category String
1694
+ * @param {string} [string=''] The string to pad.
1695
+ * @param {number} [length=0] The padding length.
1696
+ * @param {string} [chars=' '] The string used as padding.
1697
+ * @returns {string} Returns the padded string.
1698
+ * @example
1699
+ *
1700
+ * pad('abc', 8)
1701
+ * // => ' abc '
1702
+ *
1703
+ * pad('abc', 8, '_-')
1704
+ * // => '_-abc_-_'
1705
+ *
1706
+ * pad('abc', 2)
1707
+ * // => 'abc'
1708
+ */
1266
1709
  const pad = (str, length, padChar = ' ', align = 'right') => {
1267
1710
  const c = padChar;
1268
1711
  const s = str + '';
@@ -1290,20 +1733,33 @@
1290
1733
  return align === 'center' ? s.slice(0, Math.ceil(l / 2)) + e + s.slice(n - Math.floor(l / 2)) : s.slice(0, l) + e;
1291
1734
  };
1292
1735
 
1736
+ // TODO: 性能不是很好,需要优化,后续升级到最新版本的 canvas 之后就可以删除了
1737
+ /**
1738
+ * 生成uuid字符串.
1739
+ * 参考https://github.com/yangjunning/issues/issues/56
1740
+ * @param len
1741
+ * @param radix
1742
+ * @returns
1743
+ */
1293
1744
  const uuid = (len, radix) => {
1294
1745
  const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
1295
1746
  const uuid = [];
1296
1747
  let i;
1297
1748
  radix = radix || chars.length;
1298
1749
  if (len) {
1750
+ // Compact form
1299
1751
  for (i = 0; i < len; i++) {
1300
1752
  uuid[i] = chars[0 | (Math.random() * radix)];
1301
1753
  }
1302
1754
  }
1303
1755
  else {
1756
+ // rfc4122, version 4 form
1304
1757
  let r;
1758
+ // rfc4122 requires these characters
1305
1759
  uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
1306
1760
  uuid[14] = '4';
1761
+ // Fill in random data. At i==19 set the high bits of clock sequence as
1762
+ // per rfc4122, sec. 4.1.5
1307
1763
  for (i = 0; i < 36; i++) {
1308
1764
  if (!uuid[i]) {
1309
1765
  r = 0 | (Math.random() * 16);
@@ -1350,6 +1806,9 @@
1350
1806
  };
1351
1807
  }
1352
1808
 
1809
+ /**
1810
+ * @see https://github.com/lodash/lodash/blob/master/debounce.js
1811
+ */
1353
1812
  let hasRaf = false;
1354
1813
  try {
1355
1814
  hasRaf = typeof requestAnimationFrame === 'function' && typeof cancelAnimationFrame === 'function';
@@ -1363,12 +1822,14 @@
1363
1822
  let lastThis;
1364
1823
  let maxWait;
1365
1824
  let result;
1825
+ // eslint-disable-next-line no-undef
1366
1826
  let timerId;
1367
1827
  let lastCallTime;
1368
1828
  let lastInvokeTime = 0;
1369
1829
  let leading = false;
1370
1830
  let maxing = false;
1371
1831
  let trailing = true;
1832
+ // Bypass `requestAnimationFrame` by explicitly setting `wait=0`.
1372
1833
  const useRAF = !wait && wait !== 0 && hasRaf;
1373
1834
  if (typeof func !== 'function') {
1374
1835
  throw new TypeError('Expected a function');
@@ -1404,8 +1865,11 @@
1404
1865
  clearTimeout(id);
1405
1866
  }
1406
1867
  function leadingEdge(time) {
1868
+ // Reset any `maxWait` timer.
1407
1869
  lastInvokeTime = time;
1870
+ // Start the timer for the trailing edge.
1408
1871
  timerId = startTimer(timerExpired, wait);
1872
+ // Invoke the leading edge.
1409
1873
  return leading ? invokeFunc(time) : result;
1410
1874
  }
1411
1875
  function remainingWait(time) {
@@ -1417,6 +1881,9 @@
1417
1881
  function shouldInvoke(time) {
1418
1882
  const timeSinceLastCall = time - lastCallTime;
1419
1883
  const timeSinceLastInvoke = time - lastInvokeTime;
1884
+ // Either this is the first call, activity has stopped and we're at the
1885
+ // trailing edge, the system time has gone backwards and we're treating
1886
+ // it as the trailing edge, or we've hit the `maxWait` limit.
1420
1887
  return (lastCallTime === undefined ||
1421
1888
  timeSinceLastCall >= wait ||
1422
1889
  timeSinceLastCall < 0 ||
@@ -1427,10 +1894,13 @@
1427
1894
  if (shouldInvoke(time)) {
1428
1895
  return trailingEdge(time);
1429
1896
  }
1897
+ // Restart the timer.
1430
1898
  timerId = startTimer(timerExpired, remainingWait(time));
1431
1899
  }
1432
1900
  function trailingEdge(time) {
1433
1901
  timerId = undefined;
1902
+ // Only invoke if we have `lastArgs` which means `func` has been
1903
+ // debounced at least once.
1434
1904
  if (trailing && lastArgs) {
1435
1905
  return invokeFunc(time);
1436
1906
  }
@@ -1454,6 +1924,7 @@
1454
1924
  const time = Date.now();
1455
1925
  const isInvoking = shouldInvoke(time);
1456
1926
  lastArgs = args;
1927
+ // @ts-ignore
1457
1928
  lastThis = this;
1458
1929
  lastCallTime = time;
1459
1930
  if (isInvoking) {
@@ -1461,6 +1932,7 @@
1461
1932
  return leadingEdge(lastCallTime);
1462
1933
  }
1463
1934
  if (maxing) {
1935
+ // Handle invocations in a tight loop.
1464
1936
  timerId = startTimer(timerExpired, wait);
1465
1937
  return invokeFunc(lastCallTime);
1466
1938
  }
@@ -1476,6 +1948,9 @@
1476
1948
  return debounced;
1477
1949
  }
1478
1950
 
1951
+ /**
1952
+ * @see https://github.com/lodash/lodash/blob/master/throttle.js
1953
+ */
1479
1954
  function throttle(func, wait, options) {
1480
1955
  let leading = true;
1481
1956
  let trailing = true;
@@ -1525,47 +2000,55 @@
1525
2000
  };
1526
2001
  }
1527
2002
  function interpolateString(a, b) {
1528
- let bi = (reA.lastIndex = reB.lastIndex = 0);
1529
- let am;
1530
- let bm;
1531
- let bs;
1532
- let i = -1;
1533
- const s = [];
1534
- const q = [];
2003
+ let bi = (reA.lastIndex = reB.lastIndex = 0); // scan index for next number in b
2004
+ let am; // current match in a
2005
+ let bm; // current match in b
2006
+ let bs; // string preceding current number in b, if any
2007
+ let i = -1; // index in s
2008
+ const s = []; // string constants and placeholders
2009
+ const q = []; // number interpolators
2010
+ // Coerce inputs to strings.
1535
2011
  (a = a + ''), (b = b + '');
2012
+ // Interpolate pairs of numbers in a & b.
1536
2013
  while ((am = reA.exec(a)) && (bm = reB.exec(b))) {
1537
2014
  if ((bs = bm.index) > bi) {
2015
+ // a string precedes the next number in b
1538
2016
  bs = b.slice(bi, bs);
1539
2017
  if (s[i]) {
1540
2018
  s[i] += bs;
1541
- }
2019
+ } // coalesce with previous string
1542
2020
  else {
1543
2021
  s[++i] = bs;
1544
2022
  }
1545
2023
  }
1546
2024
  if ((am = am[0]) === (bm = bm[0])) {
2025
+ // numbers in a & b match
1547
2026
  if (s[i]) {
1548
2027
  s[i] += bm;
1549
- }
2028
+ } // coalesce with previous string
1550
2029
  else {
1551
2030
  s[++i] = bm;
1552
2031
  }
1553
2032
  }
1554
2033
  else {
2034
+ // interpolate non-matching numbers
1555
2035
  s[++i] = null;
1556
2036
  q.push({ i: i, x: interpolateNumber(am, bm) });
1557
2037
  }
1558
2038
  bi = reB.lastIndex;
1559
2039
  }
2040
+ // Add remains of b.
1560
2041
  if (bi < b.length) {
1561
2042
  bs = b.slice(bi);
1562
2043
  if (s[i]) {
1563
2044
  s[i] += bs;
1564
- }
2045
+ } // coalesce with previous string
1565
2046
  else {
1566
2047
  s[++i] = bs;
1567
2048
  }
1568
2049
  }
2050
+ // Special optimization for only a single match.
2051
+ // Otherwise, interpolate each of the numbers and rejoin the string.
1569
2052
  return s.length < 2
1570
2053
  ? q[0]
1571
2054
  ? one(q[0].x)
@@ -1579,19 +2062,36 @@
1579
2062
  });
1580
2063
  }
1581
2064
 
1582
- const TIME_REG = /^(?:(\d{4})(?:[-\/](\d{1,2})(?:[-\/](\d{1,2})(?:[T ](\d{1,2})(?::(\d{1,2})(?::(\d{1,2})(?:[.,](\d+))?)?)?(Z|[\+\-]\d\d:?\d\d)?)?)?)?)?$/;
2065
+ // eslint-disable-next-line no-useless-escape
2066
+ const TIME_REG = /^(?:(\d{4})(?:[-\/](\d{1,2})(?:[-\/](\d{1,2})(?:[T ](\d{1,2})(?::(\d{1,2})(?::(\d{1,2})(?:[.,](\d+))?)?)?(Z|[\+\-]\d\d:?\d\d)?)?)?)?)?$/; // eslint-disable-line
1583
2067
  function toDate(val) {
1584
2068
  if (val instanceof Date) {
1585
2069
  return val;
1586
2070
  }
1587
2071
  else if (isString(val)) {
2072
+ // Different browsers parse date in different way, so we parse it manually.
2073
+ // Some other issues:
2074
+ // new Date('1970-01-01') is UTC,
2075
+ // new Date('1970/01/01') and new Date('1970-1-01') is local.
2076
+ // See issue #3623
1588
2077
  const match = TIME_REG.exec(val);
1589
2078
  if (!match) {
2079
+ // return Invalid Date.
1590
2080
  return new Date(NaN);
1591
2081
  }
2082
+ // Use local time when no timezone offset specifed.
1592
2083
  if (!match[8]) {
2084
+ // match[n] can only be string or undefined.
2085
+ // But take care of '12' + 1 => '121'.
1593
2086
  return new Date(+match[1], +(match[2] || 1) - 1, +match[3] || 1, +match[4] || 0, +(match[5] || 0), +match[6] || 0, match[7] ? +match[7].substring(0, 3) : 0);
1594
2087
  }
2088
+ // Timezoneoffset of Javascript Date has considered DST (Daylight Saving Time,
2089
+ // https://tc39.github.io/ecma262/#sec-daylight-saving-time-adjustment).
2090
+ // For example, system timezone is set as "Time Zone: America/Toronto",
2091
+ // then these code will get different result:
2092
+ // `new Date(1478411999999).getTimezoneOffset(); // get 240`
2093
+ // `new Date(1478412000000).getTimezoneOffset(); // get 300`
2094
+ // So we should not use `new Date`, but use `Date.UTC`.
1595
2095
  let hour = +match[4] || 0;
1596
2096
  if (match[8].toUpperCase() !== 'Z') {
1597
2097
  hour -= +match[8].slice(0, 3);
@@ -1635,13 +2135,25 @@
1635
2135
  function seedRandom(seed) {
1636
2136
  return parseFloat('0.' + Math.sin(seed).toString().substring(6));
1637
2137
  }
2138
+ /* Adapted from vega by University of Washington Interactive Data Lab
2139
+ * https://vega.github.io/vega/
2140
+ * Licensed under the BSD-3-Clause
2141
+
2142
+ * url: https://github.com/vega/vega/blob/main/packages/vega-statistics/src/lcg.js
2143
+ * License: https://github.com/vega/vega/blob/main/LICENSE
2144
+ * @license
2145
+ */
2146
+ // https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use
1638
2147
  const a = 1664525;
1639
2148
  const c = 1013904223;
1640
- const m = 4294967296;
2149
+ const m = 4294967296; // 2^32
1641
2150
  function randomLCG(initS = 1) {
1642
2151
  let s = initS;
1643
2152
  return () => (s = (a * s + c) % m) / m;
1644
2153
  }
2154
+ /**
2155
+ * 随机拟合
2156
+ */
1645
2157
  const fakeRandom = () => {
1646
2158
  let i = -1;
1647
2159
  const arr = [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9];
@@ -1670,6 +2182,9 @@
1670
2182
  }
1671
2183
  return fieldSingle(fieldStr, opt);
1672
2184
  };
2185
+ /**
2186
+ * 取数逻辑
2187
+ */
1673
2188
  const simpleField = (option) => {
1674
2189
  if (!option) {
1675
2190
  return null;
@@ -1692,12 +2207,17 @@
1692
2207
 
1693
2208
  const zero = (_) => 0;
1694
2209
 
2210
+ /**
2211
+ * Return an array with minimum and maximum values, in the
2212
+ * form [min, max]. Ignores null, undefined, and NaN values.
2213
+ */
1695
2214
  const extent = (array, func) => {
1696
2215
  const valueGetter = isFunction(func) ? func : (val) => val;
1697
2216
  let min;
1698
2217
  let max;
1699
2218
  if (array && array.length) {
1700
2219
  const n = array.length;
2220
+ // find first valid value
1701
2221
  for (let i = 0; i < n; i += 1) {
1702
2222
  let value = valueGetter(array[i]);
1703
2223
  if (!isNil(value) && isNumber((value = +value)) && !Number.isNaN(value)) {
@@ -1716,49 +2236,26 @@
1716
2236
  return [min, max];
1717
2237
  };
1718
2238
 
1719
- function invNorm(p) {
1720
- if (p <= 0 || p >= 1) {
1721
- return 0;
1722
- }
1723
- const a1 = -39.6968302866538;
1724
- const a2 = 220.946098424521;
1725
- const a3 = -275.928510446969;
1726
- const a4 = 138.357751867269;
1727
- const a5 = -30.6647980661472;
1728
- const a6 = 2.50662827745924;
1729
- const b1 = -54.4760987982241;
1730
- const b2 = 161.585836858041;
1731
- const b3 = -155.698979859887;
1732
- const b4 = 66.8013118877197;
1733
- const b5 = -13.2806815528857;
1734
- const c1 = -0.00778489400243029;
1735
- const c2 = -0.322396458041136;
1736
- const c3 = -2.40075827716184;
1737
- const c4 = -2.54973253934373;
1738
- const c5 = 4.37466414146497;
1739
- const c6 = 2.93816398269878;
1740
- const d1 = 0.00778469570904146;
1741
- const d2 = 0.32246712907004;
1742
- const d3 = 2.445134137143;
1743
- const d4 = 3.75440866190742;
1744
- const pLow = 0.02425;
1745
- const pHigh = 1 - pLow;
1746
- let q;
1747
- let r;
1748
- if (p < pLow) {
1749
- q = Math.sqrt(-2 * Math.log(p));
1750
- return (((((c1 * q + c2) * q + c3) * q + c4) * q + c5) * q + c6) / ((((d1 * q + d2) * q + d3) * q + d4) * q + 1);
2239
+ /** Compute a two-sided normal critical value without subtracting a small tail probability from one. */
2240
+ function confidenceCriticalValue(alpha) {
2241
+ if (!Number.isFinite(alpha) || alpha < 0 || alpha > 1) {
2242
+ return NaN;
1751
2243
  }
1752
- if (p <= pHigh) {
1753
- q = p - 0.5;
1754
- r = q * q;
1755
- return (((((((a1 * r + a2) * r + a3) * r + a4) * r + a5) * r + a6) * q) /
1756
- (((((b1 * r + b2) * r + b3) * r + b4) * r + b5) * r + 1));
2244
+ return -normalQuantile(alpha / 2);
2245
+ }
2246
+ /** A zero standard error gives a degenerate interval even when the critical value is infinite. */
2247
+ function confidenceHalfWidth(z, standardError) {
2248
+ if (Number.isNaN(z)) {
2249
+ return NaN;
1757
2250
  }
1758
- q = Math.sqrt(-2 * Math.log(1 - p));
1759
- return -((((((c1 * q + c2) * q + c3) * q + c4) * q + c5) * q + c6) / ((((d1 * q + d2) * q + d3) * q + d4) * q + 1));
2251
+ return standardError === 0 ? 0 : z * standardError;
1760
2252
  }
2253
+ /**
2254
+ * Compute basic components used for linear regression confidence / prediction intervals.
2255
+ * - scans data to compute min/max of x, n, mean X, SSE and Sxx (sum (xi - X)^2).
2256
+ */
1761
2257
  function computeLinearCIComponents(data, x, y, predict) {
2258
+ // simple local scanner to avoid circular imports
1762
2259
  let min = Infinity;
1763
2260
  let max = -Infinity;
1764
2261
  let n = 0;
@@ -1797,6 +2294,9 @@
1797
2294
  }
1798
2295
  return { min, max, n, X, SSE, Sxx };
1799
2296
  }
2297
+ /**
2298
+ * Compute standard errors for mean and prediction at px using components.
2299
+ */
1800
2300
  function stdErrorsAt(px, comps) {
1801
2301
  const { n, X, Sxx, SSE } = comps;
1802
2302
  const s2 = n > 2 ? SSE / (n - 2) : 0;
@@ -1805,6 +2305,10 @@
1805
2305
  return { seMean, sePred };
1806
2306
  }
1807
2307
 
2308
+ /**
2309
+ * Linear regression utilities (single clean implementation).
2310
+ * Exports: ordinaryLeastSquares, visitPoints, rSquared, regressionLinear
2311
+ */
1808
2312
  function ordinaryLeastSquares(uX, uY, uXY, uX2) {
1809
2313
  const denom = uX2 - uX * uX;
1810
2314
  if (Math.abs(denom) < Number.EPSILON) {
@@ -1843,6 +2347,7 @@
1843
2347
  function regressionLinear(data, x = d => d.x, y = d => d.y, options) {
1844
2348
  var _a;
1845
2349
  const alpha = (_a = options === null || options === void 0 ? void 0 : options.alpha) !== null && _a !== void 0 ? _a : 0.05;
2350
+ // accumulate online means (sufficient statistics)
1846
2351
  let n = 0;
1847
2352
  let meanX = 0;
1848
2353
  let meanY = 0;
@@ -1881,18 +2386,20 @@
1881
2386
  if (comps.n === 0 || N <= 0) {
1882
2387
  return out;
1883
2388
  }
1884
- const z = invNorm(1 - alpha / 2);
2389
+ const z = confidenceCriticalValue(alpha);
1885
2390
  if (comps.min === comps.max) {
1886
2391
  const m = predict(comps.min);
1887
2392
  const errs = stdErrorsAt(comps.min, comps);
2393
+ const meanHalfWidth = confidenceHalfWidth(z, errs.seMean);
2394
+ const predHalfWidth = confidenceHalfWidth(z, errs.sePred);
1888
2395
  for (let i = 0; i < N; i++) {
1889
2396
  out.push({
1890
2397
  x: comps.min,
1891
2398
  mean: m,
1892
- lower: m - z * errs.seMean,
1893
- upper: m + z * errs.seMean,
1894
- predLower: m - z * errs.sePred,
1895
- predUpper: m + z * errs.sePred
2399
+ lower: m - meanHalfWidth,
2400
+ upper: m + meanHalfWidth,
2401
+ predLower: m - predHalfWidth,
2402
+ predUpper: m + predHalfWidth
1896
2403
  });
1897
2404
  }
1898
2405
  return out;
@@ -1902,13 +2409,15 @@
1902
2409
  const px = i === N - 1 ? comps.max : comps.min + step * i;
1903
2410
  const m = predict(px);
1904
2411
  const errs = stdErrorsAt(px, comps);
2412
+ const meanHalfWidth = confidenceHalfWidth(z, errs.seMean);
2413
+ const predHalfWidth = confidenceHalfWidth(z, errs.sePred);
1905
2414
  out.push({
1906
2415
  x: px,
1907
2416
  mean: m,
1908
- lower: m - z * errs.seMean,
1909
- upper: m + z * errs.seMean,
1910
- predLower: m - z * errs.sePred,
1911
- predUpper: m + z * errs.sePred
2417
+ lower: m - meanHalfWidth,
2418
+ upper: m + meanHalfWidth,
2419
+ predLower: m - predHalfWidth,
2420
+ predUpper: m + predHalfWidth
1912
2421
  });
1913
2422
  }
1914
2423
  return out;
@@ -1922,11 +2431,18 @@
1922
2431
  };
1923
2432
  }
1924
2433
 
2434
+ /**
2435
+ * Simple logistic regression (binary) using Newton-Raphson (IRLS).
2436
+ * Returns { coef, predict, evaluateGrid }
2437
+ * - coef: [intercept, beta]
2438
+ * - predict(x): probability p(y=1|x)
2439
+ */
1925
2440
  function regressionLogistic(data, x = d => d.x, y = d => d.y, options) {
1926
2441
  var _a, _b, _c;
1927
2442
  const maxIter = (_a = options === null || options === void 0 ? void 0 : options.maxIteration) !== null && _a !== void 0 ? _a : 25;
1928
2443
  const tol = (_b = options === null || options === void 0 ? void 0 : options.tol) !== null && _b !== void 0 ? _b : 1e-6;
1929
2444
  const alpha = (_c = options === null || options === void 0 ? void 0 : options.alpha) !== null && _c !== void 0 ? _c : 0.05;
2445
+ // build arrays
1930
2446
  const xs = [];
1931
2447
  const ys = [];
1932
2448
  visitPoints(data, x, y, (dx, dy) => {
@@ -1942,6 +2458,7 @@
1942
2458
  confidenceInterval: (N = 50) => []
1943
2459
  };
1944
2460
  }
2461
+ // initial coef via linear regression rough guess
1945
2462
  let intercept = 0;
1946
2463
  let beta = 0;
1947
2464
  for (let iter = 0; iter < maxIter; iter++) {
@@ -1952,6 +2469,7 @@
1952
2469
  const pi = 1 / (1 + Math.exp(-z));
1953
2470
  p[i] = pi;
1954
2471
  }
2472
+ // compute gradient and hessian
1955
2473
  let g0 = 0;
1956
2474
  let g1 = 0;
1957
2475
  let h00 = 0;
@@ -1966,6 +2484,7 @@
1966
2484
  h01 += wi * xs[i];
1967
2485
  h11 += wi * xs[i] * xs[i];
1968
2486
  }
2487
+ // solve 2x2 system H * delta = g
1969
2488
  const det = h00 * h11 - h01 * h01;
1970
2489
  if (Math.abs(det) < 1e-12) {
1971
2490
  break;
@@ -2026,18 +2545,20 @@
2026
2545
  if (comps.n === 0) {
2027
2546
  return out;
2028
2547
  }
2029
- const z = Math.abs(invNorm(1 - alpha / 2));
2548
+ const z = confidenceCriticalValue(alpha);
2030
2549
  if (comps.min === comps.max) {
2031
2550
  const v = predict(comps.min);
2032
2551
  const errs = stdErrorsAt(comps.min, comps);
2552
+ const meanHalfWidth = confidenceHalfWidth(z, errs.seMean);
2553
+ const predHalfWidth = confidenceHalfWidth(z, errs.sePred);
2033
2554
  for (let i = 0; i < N; i++) {
2034
2555
  out.push({
2035
2556
  x: comps.min,
2036
2557
  mean: v,
2037
- lower: v - z * errs.seMean,
2038
- upper: v + z * errs.seMean,
2039
- predLower: v - z * errs.sePred,
2040
- predUpper: v + z * errs.sePred
2558
+ lower: v - meanHalfWidth,
2559
+ upper: v + meanHalfWidth,
2560
+ predLower: v - predHalfWidth,
2561
+ predUpper: v + predHalfWidth
2041
2562
  });
2042
2563
  }
2043
2564
  return out;
@@ -2047,13 +2568,15 @@
2047
2568
  const px = i === N - 1 ? comps.max : comps.min + step * i;
2048
2569
  const yh = predict(px);
2049
2570
  const errs = stdErrorsAt(px, comps);
2571
+ const meanHalfWidth = confidenceHalfWidth(z, errs.seMean);
2572
+ const predHalfWidth = confidenceHalfWidth(z, errs.sePred);
2050
2573
  out.push({
2051
2574
  x: px,
2052
2575
  mean: yh,
2053
- lower: yh - z * errs.seMean,
2054
- upper: yh + z * errs.seMean,
2055
- predLower: yh - z * errs.sePred,
2056
- predUpper: yh + z * errs.sePred
2576
+ lower: yh - meanHalfWidth,
2577
+ upper: yh + meanHalfWidth,
2578
+ predLower: yh - predHalfWidth,
2579
+ predUpper: yh + predHalfWidth
2057
2580
  });
2058
2581
  }
2059
2582
  return out;
@@ -2074,12 +2597,16 @@
2074
2597
  const t = 1 - uu * uu * uu;
2075
2598
  return t * t * t;
2076
2599
  }
2600
+ /**
2601
+ * Stratified sampling to reduce data size while preserving distribution
2602
+ */
2077
2603
  function stratifiedSample(sortedData, maxSamples) {
2078
2604
  const n = sortedData.length;
2079
2605
  if (n <= maxSamples) {
2080
2606
  return sortedData;
2081
2607
  }
2082
2608
  const sampled = [];
2609
+ // More aggressive sampling - use exact step size
2083
2610
  const step = n / maxSamples;
2084
2611
  for (let i = 0; i < maxSamples; i++) {
2085
2612
  const idx = Math.min(Math.floor(i * step), n - 1);
@@ -2087,6 +2614,15 @@
2087
2614
  }
2088
2615
  return sampled;
2089
2616
  }
2617
+ /**
2618
+ * Simple lowess implementation (univariate x)
2619
+ * options:
2620
+ * - span: fraction of points used in local regression (0,1]
2621
+ * - degree: 0 (constant) or 1 (linear)
2622
+ * - iterations: number of robustifying iterations
2623
+ * - alpha: confidence level for CI
2624
+ * - maxSamples: maximum number of points to use (default: 1000 for fast processing)
2625
+ */
2090
2626
  function regressionLowess(data, x = d => d.x, y = d => d.y, options = {}) {
2091
2627
  var _a;
2092
2628
  const span = options.span || 0.3;
@@ -2094,11 +2630,13 @@
2094
2630
  const alpha = (_a = options.alpha) !== null && _a !== void 0 ? _a : 0.05;
2095
2631
  const iterations = options.iterations == null ? 2 : options.iterations;
2096
2632
  const maxSamples = options.maxSamples || 1000;
2633
+ // Collect and sort data by x
2097
2634
  const rawPoints = [];
2098
- visitPoints(data, x, y, (dx, dy) => {
2099
- rawPoints.push({ x: dx, y: dy });
2635
+ visitPoints(data, x, y, (dx, dy, index) => {
2636
+ rawPoints.push({ x: dx, y: dy, index });
2100
2637
  });
2101
2638
  rawPoints.sort((a, b) => a.x - b.x);
2639
+ // Apply sampling if needed
2102
2640
  const sampledPoints = stratifiedSample(rawPoints, maxSamples);
2103
2641
  const n = sampledPoints.length;
2104
2642
  const ptsX = new Array(n);
@@ -2107,10 +2645,14 @@
2107
2645
  ptsX[i] = sampledPoints[i].x;
2108
2646
  ptsY[i] = sampledPoints[i].y;
2109
2647
  }
2648
+ /**
2649
+ * Optimized predictSingle using binary search on pre-sorted data
2650
+ */
2110
2651
  function predictSingle(x0, robustWeights) {
2111
2652
  if (n === 0) {
2112
2653
  return 0;
2113
2654
  }
2655
+ // Binary search to find insertion point
2114
2656
  let left = 0;
2115
2657
  let right = n;
2116
2658
  while (left < right) {
@@ -2122,10 +2664,21 @@
2122
2664
  right = mid;
2123
2665
  }
2124
2666
  }
2125
- const m = Math.max(2, Math.min(n, Math.floor(span * n)));
2126
- const start = Math.max(0, left - Math.floor(m / 2));
2127
- const end = Math.min(n, start + m);
2128
- const actualStart = Math.max(0, end - m);
2667
+ const m = Math.min(n, Math.max(2, Math.floor(span * n)));
2668
+ // Expand by distance: equal numbers on either side need not be the nearest points.
2669
+ let lo = left - 1;
2670
+ let hi = left;
2671
+ for (let count = 0; count < m; count++) {
2672
+ if (lo >= 0 && (hi >= n || x0 - ptsX[lo] <= ptsX[hi] - x0)) {
2673
+ lo--;
2674
+ }
2675
+ else {
2676
+ hi++;
2677
+ }
2678
+ }
2679
+ const actualStart = lo + 1;
2680
+ const end = hi;
2681
+ // Find max distance and compute weights in single pass
2129
2682
  let maxDist = 0;
2130
2683
  const windowSize = end - actualStart;
2131
2684
  const distances = new Array(windowSize);
@@ -2136,6 +2689,7 @@
2136
2689
  maxDist = dist;
2137
2690
  }
2138
2691
  }
2692
+ // Compute weights using pre-calculated distances
2139
2693
  let sumw = 0;
2140
2694
  const w = new Array(windowSize);
2141
2695
  for (let i = 0; i < windowSize; i++) {
@@ -2148,7 +2702,17 @@
2148
2702
  sumw += wi;
2149
2703
  }
2150
2704
  if (sumw === 0) {
2151
- const nearestIdx = left < n ? left : n - 1;
2705
+ // Fall back to the nearest y, preserving input order for equal distances.
2706
+ let nearestIdx = 0;
2707
+ let nearestDistance = Math.abs(ptsX[0] - x0);
2708
+ for (let i = 1; i < n; i++) {
2709
+ const distance = Math.abs(ptsX[i] - x0);
2710
+ if (distance < nearestDistance ||
2711
+ (distance === nearestDistance && sampledPoints[i].index < sampledPoints[nearestIdx].index)) {
2712
+ nearestIdx = i;
2713
+ nearestDistance = distance;
2714
+ }
2715
+ }
2152
2716
  return ptsY[nearestIdx];
2153
2717
  }
2154
2718
  if (degree === 0) {
@@ -2158,6 +2722,7 @@
2158
2722
  }
2159
2723
  return s / sumw;
2160
2724
  }
2725
+ // weighted linear regression on local points
2161
2726
  let sw = 0;
2162
2727
  let sx = 0;
2163
2728
  let sy = 0;
@@ -2210,16 +2775,21 @@
2210
2775
  return out;
2211
2776
  }
2212
2777
  const step = (max - min) / (N - 1);
2778
+ // optionally add robust iterations
2213
2779
  let robustWeights;
2780
+ // Disable robustness iterations for large datasets to improve performance
2781
+ // Users can override by setting iterations explicitly
2214
2782
  const effectiveIterations = options.iterations != null ? iterations : n > 500 ? 0 : iterations;
2215
2783
  if (effectiveIterations > 0) {
2216
2784
  for (let iter = 0; iter < effectiveIterations; iter++) {
2785
+ // compute fits - pre-allocate arrays
2217
2786
  const fits = new Array(n);
2218
2787
  const res = new Array(n);
2219
2788
  for (let i = 0; i < n; i++) {
2220
2789
  fits[i] = predictSingle(ptsX[i], robustWeights);
2221
2790
  res[i] = Math.abs(ptsY[i] - fits[i]);
2222
2791
  }
2792
+ // median absolute deviation
2223
2793
  const sortedRes = res.slice().sort((a, b) => a - b);
2224
2794
  const med = sortedRes[Math.floor(n / 2)] || 0;
2225
2795
  robustWeights = new Array(n);
@@ -2244,6 +2814,7 @@
2244
2814
  if (n === 0) {
2245
2815
  return out;
2246
2816
  }
2817
+ // use data x-range (already sorted)
2247
2818
  const min = ptsX[0];
2248
2819
  const max = ptsX[n - 1];
2249
2820
  if (min === undefined || max === undefined || min === Infinity || max === -Infinity) {
@@ -2253,18 +2824,20 @@
2253
2824
  if (comps.n === 0) {
2254
2825
  return out;
2255
2826
  }
2256
- const z = Math.abs(invNorm(1 - alpha / 2));
2827
+ const z = confidenceCriticalValue(alpha);
2257
2828
  if (comps.min === comps.max) {
2258
2829
  const v = predictSingle(comps.min);
2259
2830
  const errs = stdErrorsAt(comps.min, comps);
2831
+ const meanHalfWidth = confidenceHalfWidth(z, errs.seMean);
2832
+ const predHalfWidth = confidenceHalfWidth(z, errs.sePred);
2260
2833
  for (let i = 0; i < N; i++) {
2261
2834
  out.push({
2262
2835
  x: comps.min,
2263
2836
  mean: v,
2264
- lower: v - z * errs.seMean,
2265
- upper: v + z * errs.seMean,
2266
- predLower: v - z * errs.sePred,
2267
- predUpper: v + z * errs.sePred
2837
+ lower: v - meanHalfWidth,
2838
+ upper: v + meanHalfWidth,
2839
+ predLower: v - predHalfWidth,
2840
+ predUpper: v + predHalfWidth
2268
2841
  });
2269
2842
  }
2270
2843
  return out;
@@ -2274,13 +2847,15 @@
2274
2847
  const px = i === N - 1 ? max : min + step * i;
2275
2848
  const yh = predictSingle(px);
2276
2849
  const errs = stdErrorsAt(px, comps);
2850
+ const meanHalfWidth = confidenceHalfWidth(z, errs.seMean);
2851
+ const predHalfWidth = confidenceHalfWidth(z, errs.sePred);
2277
2852
  out.push({
2278
2853
  x: px,
2279
2854
  mean: yh,
2280
- lower: yh - z * errs.seMean,
2281
- upper: yh + z * errs.seMean,
2282
- predLower: yh - z * errs.sePred,
2283
- predUpper: yh + z * errs.sePred
2855
+ lower: yh - meanHalfWidth,
2856
+ upper: yh + meanHalfWidth,
2857
+ predLower: yh - predHalfWidth,
2858
+ predUpper: yh + predHalfWidth
2284
2859
  });
2285
2860
  }
2286
2861
  return out;
@@ -2294,13 +2869,16 @@
2294
2869
  }
2295
2870
 
2296
2871
  function solveLinearSystem(A, b) {
2872
+ // Gaussian elimination with partial pivoting
2297
2873
  const n = b.length;
2874
+ // clone
2298
2875
  const M = new Array(n);
2299
2876
  for (let i = 0; i < n; i++) {
2300
2877
  M[i] = A[i].slice();
2301
2878
  M[i].push(b[i]);
2302
2879
  }
2303
2880
  for (let k = 0; k < n; k++) {
2881
+ // find pivot
2304
2882
  let maxRow = k;
2305
2883
  let maxVal = Math.abs(M[k][k]);
2306
2884
  for (let i = k + 1; i < n; i++) {
@@ -2315,14 +2893,18 @@
2315
2893
  M[k] = M[maxRow];
2316
2894
  M[maxRow] = tmp;
2317
2895
  }
2896
+ // singular check
2318
2897
  if (Math.abs(M[k][k]) < 1e-12) {
2898
+ // return least squares fallback zeros
2319
2899
  const res = new Array(n).fill(0);
2320
2900
  return res;
2321
2901
  }
2902
+ // normalize row
2322
2903
  for (let j = k + 1; j <= n; j++) {
2323
2904
  M[k][j] = M[k][j] / M[k][k];
2324
2905
  }
2325
2906
  M[k][k] = 1;
2907
+ // eliminate
2326
2908
  for (let i = 0; i < n; i++) {
2327
2909
  if (i === k) {
2328
2910
  continue;
@@ -2359,6 +2941,7 @@
2359
2941
  xp *= dx;
2360
2942
  }
2361
2943
  });
2944
+ // build normal matrix
2362
2945
  const A = new Array(m);
2363
2946
  for (let i = 0; i < m; i++) {
2364
2947
  A[i] = new Array(m).fill(0);
@@ -2389,6 +2972,7 @@
2389
2972
  coef,
2390
2973
  predict,
2391
2974
  rSquared: rSquared(data, x, y, (() => {
2975
+ // compute mean y
2392
2976
  let sum = 0;
2393
2977
  let cnt = 0;
2394
2978
  visitPoints(data, x, y, (_dx, dy) => {
@@ -2402,6 +2986,7 @@
2402
2986
  if (N <= 0) {
2403
2987
  return out;
2404
2988
  }
2989
+ // compute range
2405
2990
  let min = Infinity;
2406
2991
  let max = -Infinity;
2407
2992
  visitPoints(data, x, y, dx => {
@@ -2440,18 +3025,20 @@
2440
3025
  if (comps.n === 0) {
2441
3026
  return out;
2442
3027
  }
2443
- const z = Math.abs(invNorm(1 - alpha / 2));
3028
+ const z = confidenceCriticalValue(alpha);
2444
3029
  if (comps.min === comps.max) {
2445
3030
  const v = predict(comps.min);
2446
3031
  const errs = stdErrorsAt(comps.min, comps);
3032
+ const meanHalfWidth = confidenceHalfWidth(z, errs.seMean);
3033
+ const predHalfWidth = confidenceHalfWidth(z, errs.sePred);
2447
3034
  for (let i = 0; i < N; i++) {
2448
3035
  out.push({
2449
3036
  x: comps.min,
2450
3037
  mean: v,
2451
- lower: v - z * errs.seMean,
2452
- upper: v + z * errs.seMean,
2453
- predLower: v - z * errs.sePred,
2454
- predUpper: v + z * errs.sePred
3038
+ lower: v - meanHalfWidth,
3039
+ upper: v + meanHalfWidth,
3040
+ predLower: v - predHalfWidth,
3041
+ predUpper: v + predHalfWidth
2455
3042
  });
2456
3043
  }
2457
3044
  return out;
@@ -2461,13 +3048,15 @@
2461
3048
  const px = i === N - 1 ? comps.max : comps.min + step * i;
2462
3049
  const yh = predict(px);
2463
3050
  const errs = stdErrorsAt(px, comps);
3051
+ const meanHalfWidth = confidenceHalfWidth(z, errs.seMean);
3052
+ const predHalfWidth = confidenceHalfWidth(z, errs.sePred);
2464
3053
  out.push({
2465
3054
  x: px,
2466
3055
  mean: yh,
2467
- lower: yh - z * errs.seMean,
2468
- upper: yh + z * errs.seMean,
2469
- predLower: yh - z * errs.sePred,
2470
- predUpper: yh + z * errs.sePred
3056
+ lower: yh - meanHalfWidth,
3057
+ upper: yh + meanHalfWidth,
3058
+ predLower: yh - predHalfWidth,
3059
+ predUpper: yh + predHalfWidth
2471
3060
  });
2472
3061
  }
2473
3062
  return out;
@@ -2475,14 +3064,29 @@
2475
3064
  };
2476
3065
  }
2477
3066
 
3067
+ /**
3068
+ * Kernel Density Estimation (KDE) utilities
3069
+ *
3070
+ * Exports:
3071
+ * - Kernel functions: gaussian, epanechnikov
3072
+ * - bandwidth selectors: scott, silverman
3073
+ * - kde: main function to compute density estimates at provided points
3074
+ */
3075
+ /** Gaussian kernel (standard normal) */
2478
3076
  const gaussian = (u) => {
2479
3077
  const invSqrt2Pi = 1 / Math.sqrt(2 * Math.PI);
2480
3078
  return invSqrt2Pi * Math.exp(-0.5 * u * u);
2481
3079
  };
3080
+ /** Epanechnikov kernel (compact support) */
2482
3081
  const epanechnikov = (u) => {
2483
3082
  const absu = Math.abs(u);
2484
3083
  return absu <= 1 ? 0.75 * (1 - u * u) : 0;
2485
3084
  };
3085
+ /**
3086
+ * Bandwidth selectors
3087
+ * - scott: h = n^{-1/(d+4)} * std
3088
+ * - silverman: h = ( (4/(d+2))^{1/(d+4)} ) * n^{-1/(d+4)} * std
3089
+ */
2486
3090
  function scott(n, std, dim = 1) {
2487
3091
  if (n <= 0 || std === 0) {
2488
3092
  return 0;
@@ -2496,6 +3100,9 @@
2496
3100
  const factor = Math.pow(4 / (dim + 2), 1 / (dim + 4));
2497
3101
  return factor * std * Math.pow(n, -1 / (dim + 4));
2498
3102
  }
3103
+ /**
3104
+ * Compute standard deviation for numeric array
3105
+ */
2499
3106
  function std(values) {
2500
3107
  const n = values.length;
2501
3108
  if (n === 0) {
@@ -2513,6 +3120,10 @@
2513
3120
  }
2514
3121
  return Math.sqrt(s / n);
2515
3122
  }
3123
+ /**
3124
+ * Factory: create a KDE evaluator for given data and options.
3125
+ * Usage: const model = kde(data, options); model.evaluate(x) or model.evaluate([x1,x2])
3126
+ */
2516
3127
  function kde(data, options = {}) {
2517
3128
  const n = data.length;
2518
3129
  const kernel = options.kernel || gaussian;
@@ -2527,6 +3138,7 @@
2527
3138
  h = scott(n, sd, 1);
2528
3139
  }
2529
3140
  }
3141
+ // if still zero (constant data), evaluator returns zeros
2530
3142
  if (!h || h <= 0) {
2531
3143
  const zerosEval = (x) => {
2532
3144
  if (Array.isArray(x)) {
@@ -2543,6 +3155,7 @@
2543
3155
  if (N <= 0) {
2544
3156
  return out;
2545
3157
  }
3158
+ // compute data min (use data range if available)
2546
3159
  let min = Infinity;
2547
3160
  let max = -Infinity;
2548
3161
  for (let j = 0; j < n; j++) {
@@ -2599,6 +3212,7 @@
2599
3212
  if (N <= 0) {
2600
3213
  return out;
2601
3214
  }
3215
+ // compute data range
2602
3216
  let min = Infinity;
2603
3217
  let max = -Infinity;
2604
3218
  for (let i = 0; i < n; i++) {
@@ -2614,6 +3228,7 @@
2614
3228
  return out;
2615
3229
  }
2616
3230
  if (min === max) {
3231
+ // single point repeated
2617
3232
  for (let i = 0; i < N; i++) {
2618
3233
  out.push({ x: min, y: evalPoint(min) });
2619
3234
  }
@@ -2629,6 +3244,14 @@
2629
3244
  };
2630
3245
  }
2631
3246
 
3247
+ /**
3248
+ * Empirical Cumulative Distribution Function (ECDF)
3249
+ * Returns a factory that builds an evaluator over provided samples.
3250
+ * The evaluator exposes:
3251
+ * - evaluate(x:number) => proportion of samples <= x
3252
+ * - evaluate(xs:number[]) => number[]
3253
+ * - evaluateGrid(N:number) => { points: number[], cdf: number[] } over sample range
3254
+ */
2632
3255
  function ecdf(data) {
2633
3256
  const n = data.length;
2634
3257
  const sorted = data.slice().sort((a, b) => a - b);
@@ -2636,8 +3259,9 @@
2636
3259
  if (n === 0) {
2637
3260
  return 0;
2638
3261
  }
3262
+ // count of values <= x (upper bound index)
2639
3263
  let lo = 0;
2640
- let hi = n;
3264
+ let hi = n; // exclusive
2641
3265
  while (lo < hi) {
2642
3266
  const mid = (lo + hi) >>> 1;
2643
3267
  if (sorted[mid] <= x) {
@@ -2689,8 +3313,192 @@
2689
3313
  };
2690
3314
  }
2691
3315
 
3316
+ function pearson(x, y) {
3317
+ let sx = 0;
3318
+ let sy = 0;
3319
+ for (let i = 0; i < x.length; i++) {
3320
+ sx = Math.max(sx, Math.abs(x[i]));
3321
+ sy = Math.max(sy, Math.abs(y[i]));
3322
+ }
3323
+ if (!sx || !sy) {
3324
+ return undefined;
3325
+ }
3326
+ let mx = 0;
3327
+ let my = 0;
3328
+ let cx = 0;
3329
+ let cy = 0;
3330
+ for (let i = 0; i < x.length; i++) {
3331
+ const vx = x[i] / sx - cx;
3332
+ const vy = y[i] / sy - cy;
3333
+ const tx = mx + vx;
3334
+ const ty = my + vy;
3335
+ cx = tx - mx - vx;
3336
+ cy = ty - my - vy;
3337
+ mx = tx;
3338
+ my = ty;
3339
+ }
3340
+ mx /= x.length;
3341
+ my /= x.length;
3342
+ let xx = 0;
3343
+ let yy = 0;
3344
+ let xy = 0;
3345
+ for (let i = 0; i < x.length; i++) {
3346
+ const dx = x[i] / sx - mx;
3347
+ const dy = y[i] / sy - my;
3348
+ xx += dx * dx;
3349
+ yy += dy * dy;
3350
+ xy += dx * dy;
3351
+ }
3352
+ if (xx === 0 || yy === 0) {
3353
+ return undefined;
3354
+ }
3355
+ const result = xy / Math.sqrt(xx * yy);
3356
+ // 仅修正浮点舍入越过理论端点的几个 ulp。
3357
+ if (Math.abs(result) > 1 && Math.abs(result) <= 1 + 8 * Number.EPSILON) {
3358
+ return Math.sign(result);
3359
+ }
3360
+ return result;
3361
+ }
3362
+ function ranks(values) {
3363
+ const order = values.map((value, index) => ({ value, index })).sort((a, b) => a.value - b.value);
3364
+ const result = new Array(values.length);
3365
+ for (let start = 0; start < order.length;) {
3366
+ let end = start + 1;
3367
+ while (end < order.length && order[end].value === order[start].value) {
3368
+ end++;
3369
+ }
3370
+ const rank = (start + end + 1) / 2;
3371
+ for (let i = start; i < end; i++) {
3372
+ result[order[i].index] = rank;
3373
+ }
3374
+ start = end;
3375
+ }
3376
+ return result;
3377
+ }
3378
+ function kendall(x, y) {
3379
+ const pairs = x.map((value, index) => ({ x: value, y: y[index] })).sort((a, b) => a.x - b.x || a.y - b.y);
3380
+ const sortedY = [...y].sort((a, b) => a - b);
3381
+ const yRanks = new Map();
3382
+ let tiesY = 0;
3383
+ for (let start = 0; start < sortedY.length;) {
3384
+ let end = start + 1;
3385
+ while (end < sortedY.length && sortedY[end] === sortedY[start]) {
3386
+ end++;
3387
+ }
3388
+ tiesY += ((end - start) * (end - start - 1)) / 2;
3389
+ yRanks.set(sortedY[start], yRanks.size + 1);
3390
+ start = end;
3391
+ }
3392
+ const tree = new Float64Array(yRanks.size + 1);
3393
+ let tiesX = 0;
3394
+ let tiesBoth = 0;
3395
+ let discordant = 0;
3396
+ for (let start = 0; start < pairs.length;) {
3397
+ let end = start + 1;
3398
+ while (end < pairs.length && pairs[end].x === pairs[start].x) {
3399
+ end++;
3400
+ }
3401
+ tiesX += ((end - start) * (end - start - 1)) / 2;
3402
+ for (let i = start; i < end;) {
3403
+ let j = i + 1;
3404
+ while (j < end && pairs[j].y === pairs[i].y) {
3405
+ j++;
3406
+ }
3407
+ tiesBoth += ((j - i) * (j - i - 1)) / 2;
3408
+ i = j;
3409
+ }
3410
+ // 同 x 组先查询再更新,避免将 x ties 算成逆序。
3411
+ for (let i = start; i < end; i++) {
3412
+ let lessOrEqual = 0;
3413
+ for (let rank = yRanks.get(pairs[i].y); rank > 0; rank -= rank & -rank) {
3414
+ lessOrEqual += tree[rank];
3415
+ }
3416
+ discordant += start - lessOrEqual;
3417
+ }
3418
+ for (let i = start; i < end; i++) {
3419
+ for (let rank = yRanks.get(pairs[i].y); rank < tree.length; rank += rank & -rank) {
3420
+ tree[rank]++;
3421
+ }
3422
+ }
3423
+ start = end;
3424
+ }
3425
+ const total = (x.length * (x.length - 1)) / 2;
3426
+ if (total === tiesX || total === tiesY) {
3427
+ return undefined;
3428
+ }
3429
+ return (total - tiesX - tiesY + tiesBoth - 2 * discordant) / Math.sqrt((total - tiesX) * (total - tiesY));
3430
+ }
3431
+ /** 等长有限样本的相关系数;常量/不足两条返回 undefined,不修改数组。 */
3432
+ function correlation(x, y, method = 'pearson') {
3433
+ if (x.length !== y.length ||
3434
+ !['pearson', 'spearman', 'kendall'].includes(method) ||
3435
+ x.some(value => !Number.isFinite(value)) ||
3436
+ y.some(value => !Number.isFinite(value))) {
3437
+ throw new RangeError('correlation requires equal finite arrays and a supported method');
3438
+ }
3439
+ if (x.length < 2) {
3440
+ return undefined;
3441
+ }
3442
+ return method === 'kendall' ? kendall(x, y) : method === 'spearman' ? pearson(ranks(x), ranks(y)) : pearson(x, y);
3443
+ }
3444
+
3445
+ /** 有限数值的均值;空输入没有均值,不修改输入。 */
3446
+ function mean(values) {
3447
+ if (!values.length) {
3448
+ return undefined;
3449
+ }
3450
+ let scale = 0;
3451
+ for (const value of values) {
3452
+ if (!Number.isFinite(value)) {
3453
+ throw new RangeError('mean requires finite numbers');
3454
+ }
3455
+ scale = Math.max(scale, Math.abs(value));
3456
+ }
3457
+ if (scale === 0) {
3458
+ return 0;
3459
+ }
3460
+ let sum = 0;
3461
+ let correction = 0;
3462
+ for (const value of values) {
3463
+ const next = value / scale;
3464
+ const total = sum + next;
3465
+ // Neumaier compensation retains small contributions even when larger values cancel.
3466
+ correction += Math.abs(sum) >= Math.abs(next) ? sum - total + next : next - total + sum;
3467
+ sum = total;
3468
+ }
3469
+ return ((sum + correction) / values.length) * scale;
3470
+ }
3471
+
3472
+ /** 有放回重采样;回调不得修改或持有复用的只读样本缓冲区。 */
3473
+ function bootstrapEstimate(values, estimator, options) {
3474
+ if (!values.length ||
3475
+ values.some(value => !Number.isFinite(value)) ||
3476
+ !Number.isInteger(options.resamples) ||
3477
+ options.resamples < 1 ||
3478
+ !Number.isInteger(options.seed) ||
3479
+ options.seed < 0 ||
3480
+ options.seed > 0xffffffff) {
3481
+ throw new RangeError('bootstrap requires finite observations, positive resamples and a uint32 seed');
3482
+ }
3483
+ const random = randomLCG(options.seed);
3484
+ const sample = new Array(values.length);
3485
+ const estimates = new Array(options.resamples);
3486
+ for (let b = 0; b < options.resamples; b++) {
3487
+ for (let i = 0; i < sample.length; i++) {
3488
+ sample[i] = values[Math.floor(random() * values.length)];
3489
+ }
3490
+ const estimate = estimator(sample);
3491
+ if (!Number.isFinite(estimate)) {
3492
+ throw new RangeError('bootstrap estimator must return a finite number');
3493
+ }
3494
+ estimates[b] = estimate;
3495
+ }
3496
+ return estimates;
3497
+ }
3498
+
2692
3499
  class HashValue {
2693
3500
  }
3501
+ // 基于 array 和 object的 HashTable
2694
3502
  class HashTable {
2695
3503
  constructor() {
2696
3504
  this.items = {};
@@ -2726,6 +3534,10 @@
2726
3534
  this.resetIndex();
2727
3535
  }
2728
3536
  }
3537
+ /**
3538
+ * 不包含当前index
3539
+ * @param index
3540
+ */
2729
3541
  delFrom(index) {
2730
3542
  for (let i = index + 1; i < this.count(); i++) {
2731
3543
  const key = this.itemList[i].key;
@@ -2769,6 +3581,7 @@
2769
3581
  getKeyByIndex(index) {
2770
3582
  return this.itemList[index].key;
2771
3583
  }
3584
+ //遍历 扩展
2772
3585
  foreach(callback) {
2773
3586
  for (const key in this.items) {
2774
3587
  const returnVal = callback(key, this.items[key].value);
@@ -2795,12 +3608,14 @@
2795
3608
  }
2796
3609
  return false;
2797
3610
  }
3611
+ //获取index
2798
3612
  indexOf(key) {
2799
3613
  if (this.has(key)) {
2800
3614
  return this.items[key].index;
2801
3615
  }
2802
3616
  return -1;
2803
3617
  }
3618
+ //插入
2804
3619
  insertAt(index, value, key) {
2805
3620
  const hashV = new HashValue();
2806
3621
  hashV.index = index;
@@ -2833,6 +3648,7 @@
2833
3648
  return returnArr;
2834
3649
  }
2835
3650
  toImmutableMap() {
3651
+ // do nothing
2836
3652
  }
2837
3653
  }
2838
3654
 
@@ -2859,6 +3675,15 @@
2859
3675
  function asin(x) {
2860
3676
  return x >= 1 ? halfPi : x <= -1 ? -halfPi : Math.asin(x);
2861
3677
  }
3678
+ /**
3679
+ * 根据比例获直线上的点
3680
+ * @param {number} x1 起始点 x
3681
+ * @param {number} y1 起始点 y
3682
+ * @param {number} x2 结束点 x
3683
+ * @param {number} y2 结束点 y
3684
+ * @param {number} t 指定比例
3685
+ * @return {object} 包含 x, y 的点
3686
+ */
2862
3687
  function pointAt(x1, y1, x2, y2, t) {
2863
3688
  let x = x2;
2864
3689
  let y = y2;
@@ -2874,6 +3699,7 @@
2874
3699
  };
2875
3700
  }
2876
3701
  function lengthFromPointToLine(point, point1, point2) {
3702
+ // 面积
2877
3703
  const dir1X = point2.x - point1.x;
2878
3704
  const dir1Y = point2.y - point1.y;
2879
3705
  const dir2X = point.x - point1.x;
@@ -2918,6 +3744,9 @@
2918
3744
 
2919
3745
  class Point {
2920
3746
  constructor(x = 0, y = 0, x1, y1) {
3747
+ /**
3748
+ * 面积图的下点
3749
+ */
2921
3750
  this.x = 0;
2922
3751
  this.y = 0;
2923
3752
  this.x = x;
@@ -2970,6 +3799,7 @@
2970
3799
  }
2971
3800
  }
2972
3801
  class PointService {
3802
+ /* distance */
2973
3803
  static distancePP(p1, p2) {
2974
3804
  return sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2));
2975
3805
  }
@@ -2979,6 +3809,7 @@
2979
3809
  static distancePN(point, x, y) {
2980
3810
  return sqrt(pow(x - point.x, 2) + pow(y - point.y, 2));
2981
3811
  }
3812
+ /* point at */
2982
3813
  static pointAtPP(p1, p2, t) {
2983
3814
  return new Point((p2.x - p1.x) * t + p1.x, (p2.y - p1.y) * t + p1.y);
2984
3815
  }
@@ -3027,12 +3858,30 @@
3027
3858
  return new ImageData(flipClampBuffer, width, height);
3028
3859
  };
3029
3860
 
3861
+ /**
3862
+ * 处理角度相关的数据计算
3863
+ */
3864
+ /**
3865
+ * degree -> radian
3866
+ * @param degree
3867
+ * @returns radian
3868
+ */
3030
3869
  function degreeToRadian(degree) {
3031
3870
  return degree * (Math.PI / 180);
3032
3871
  }
3872
+ /**
3873
+ * radian -> degree
3874
+ * @param radian
3875
+ * @returns
3876
+ */
3033
3877
  function radianToDegree(radian) {
3034
3878
  return (radian * 180) / Math.PI;
3035
3879
  }
3880
+ /**
3881
+ * 对弧度进行格式化
3882
+ * @param a 传入的弧度
3883
+ * @returns
3884
+ */
3036
3885
  const clampRadian = (angle = 0) => {
3037
3886
  if (angle < 0) {
3038
3887
  while (angle < -tau) {
@@ -3046,14 +3895,28 @@
3046
3895
  }
3047
3896
  return angle;
3048
3897
  };
3898
+ // alias of clampRadian
3049
3899
  const clampAngleByRadian = clampRadian;
3900
+ /**
3901
+ * 对角度进行格式化
3902
+ * @param a 角度
3903
+ * @returns
3904
+ */
3050
3905
  const clampDegree = (a = 0) => {
3051
3906
  if (a > 360 || a < -360) {
3052
3907
  return a % 360;
3053
3908
  }
3054
3909
  return a;
3055
3910
  };
3911
+ // alias of clampDegree
3056
3912
  const clampAngleByDegree = clampDegree;
3913
+ /**
3914
+ * 根据弧度及半径计算极坐标系下的坐标点
3915
+ * @param centerX 圆心坐标
3916
+ * @param radius 半径
3917
+ * @param angleInRadian 弧度
3918
+ * @returns 返回笛卡尔坐标点
3919
+ */
3057
3920
  function polarToCartesian(center, radius, angleInRadian) {
3058
3921
  if (!radius) {
3059
3922
  return { x: center.x, y: center.y };
@@ -3063,6 +3926,14 @@
3063
3926
  y: center.y + radius * Math.sin(angleInRadian)
3064
3927
  };
3065
3928
  }
3929
+ /**
3930
+ * 根据笛卡尔坐标系坐标计算极坐标坐标点
3931
+ * @param point
3932
+ * @param center 极坐标系中心点坐标
3933
+ * @param startAngle 极坐标系起始角度
3934
+ * @param endAngle 极坐标系结束角度
3935
+ * @returns 极坐标系坐标点
3936
+ */
3066
3937
  function cartesianToPolar(point, center = { x: 0, y: 0 }, startAngle = 0, endAngle = 2 * Math.PI) {
3067
3938
  const { x, y } = point;
3068
3939
  const { x: centerX, y: centerY } = center;
@@ -3093,9 +3964,19 @@
3093
3964
  angle: radian
3094
3965
  };
3095
3966
  }
3967
+ /**
3968
+ * 根据点的笛卡尔坐标获取该点与圆心的连线同正 x 轴方向的夹角
3969
+ * @param center 圆心坐标
3970
+ * @param point 待求的点坐标
3971
+ * @returns 返回夹角对应的弧度值
3972
+ */
3096
3973
  function getAngleByPoint(center, point) {
3097
3974
  return Math.atan2(point.y - center.y, point.x - center.x);
3098
3975
  }
3976
+ /**
3977
+ * 角度标准化处理
3978
+ * @param angle 弧度角
3979
+ */
3099
3980
  function normalizeAngle(angle) {
3100
3981
  while (angle < 0) {
3101
3982
  angle += Math.PI * 2;
@@ -3105,6 +3986,14 @@
3105
3986
  }
3106
3987
  return angle;
3107
3988
  }
3989
+ /**
3990
+ * 指定开始角度和结束角度,计算这个范围内的边界角度,
3991
+ * 即起始角度以及经过的东、南、西、北四个方向的角度
3992
+ * 计算这个角度,可以用于计算一个弧度的bounding box 等
3993
+ * @param startAngle 起始角度的弧度值
3994
+ * @param endAngle 结束角度的弧度值
3995
+ * @returns 边界角度数组
3996
+ */
3108
3997
  function findBoundaryAngles(startAngle, endAngle) {
3109
3998
  const deltaAngle = Math.abs(endAngle - startAngle);
3110
3999
  if (deltaAngle >= 2 * Math.PI || 2 * Math.PI - deltaAngle < 1e-6) {
@@ -3123,6 +4012,14 @@
3123
4012
  }
3124
4013
  return steps;
3125
4014
  }
4015
+ /**
4016
+ * 计算指定范围内,指定中心的情况下,不超出边界的最大可用半径
4017
+ * @param rect 矩形的大小
4018
+ * @param center 中心点
4019
+ * @param startAngle 起始角度的弧度值
4020
+ * @param endAngle 结束角度的弧度值
4021
+ * @returns 最大半径
4022
+ */
3126
4023
  function calculateMaxRadius(rect, center, startAngle, endAngle) {
3127
4024
  const { x, y } = center;
3128
4025
  const steps = findBoundaryAngles(startAngle, endAngle);
@@ -3174,10 +4071,18 @@
3174
4071
  return 1;
3175
4072
  }
3176
4073
 
4074
+ /* 用于判断2d相交 */
3177
4075
  function sub(out, v1, v2) {
3178
4076
  out[0] = v1[0] - v2[0];
3179
4077
  out[1] = v1[1] - v2[1];
3180
4078
  }
4079
+ /**
4080
+ * 判断直线是否相交,投影法
4081
+ * @param left1
4082
+ * @param right1
4083
+ * @param left2
4084
+ * @param right2
4085
+ */
3181
4086
  function isIntersect(left1, right1, left2, right2) {
3182
4087
  let min1 = left1[0];
3183
4088
  let max1 = right1[0];
@@ -3204,6 +4109,15 @@
3204
4109
  }
3205
4110
  return true;
3206
4111
  }
4112
+ /**
4113
+ * 获取直线交点
4114
+ * 不相交返回false,共线返回true,相交返回交点
4115
+ * https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect/565282#565282
4116
+ * @param left1
4117
+ * @param right1
4118
+ * @param left2
4119
+ * @param right2
4120
+ */
3207
4121
  function getIntersectPoint(left1, right1, left2, right2) {
3208
4122
  if (!isIntersect(left1, right1, left2, right2)) {
3209
4123
  return false;
@@ -3213,9 +4127,16 @@
3213
4127
  const tempVec = [0, 0];
3214
4128
  sub(dir1, right1, left1);
3215
4129
  sub(dir2, right2, left2);
4130
+ // 判断共线
3216
4131
  if (fuzzyEqualVec(dir1, dir2)) {
3217
4132
  return true;
3218
4133
  }
4134
+ // https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect/565282#565282
4135
+ // line1: left1 + dir1 * t
4136
+ // line2: left2 + dir2 * u
4137
+ // 当 left1 + dir1 * t = left2 + dir2 * u => (left1 + dir1 * t) x dir2 = (left2 + dir2 * u) x dir2
4138
+ // => dir1 x dir2 * t = (left2 - left1) x dir2 => t = (left2 - left1) x dir2 / (dir1 x dir2)
4139
+ // 直线不平行,dir1 x dir2 ≠ 0,当0 <= t <= 1时,可以求出交点
3219
4140
  sub(tempVec, left2, left1);
3220
4141
  const t = crossProduct(tempVec, dir2) / crossProduct(dir1, dir2);
3221
4142
  if (t >= 0 && t <= 1) {
@@ -3223,6 +4144,13 @@
3223
4144
  }
3224
4145
  return false;
3225
4146
  }
4147
+ /**
4148
+ * 获取两个rect的相交部分
4149
+ * 如果有bbox为null,返回null,如果不相交返回{x1: 0, y1: 0, x2: 0, y2: 0}
4150
+ * @param bbox1
4151
+ * @param bbox2
4152
+ * @param format
4153
+ */
3226
4154
  function getRectIntersect(bbox1, bbox2, format) {
3227
4155
  if (bbox1 === null) {
3228
4156
  return bbox2;
@@ -3267,19 +4195,35 @@
3267
4195
  }
3268
4196
  return { x11, x12, y11, y12, x21, x22, y21, y22 };
3269
4197
  };
4198
+ /**
4199
+ * 矩形是否在另一个矩形内部
4200
+ * 返回InnerBBox
4201
+ * @param bbox1
4202
+ * @param bbox2
4203
+ * @param format
4204
+ */
3270
4205
  function rectInsideAnotherRect(bbox1, bbox2, format) {
3271
4206
  if (!bbox1 || !bbox2) {
3272
4207
  return exports.InnerBBox.NONE;
3273
4208
  }
3274
4209
  const { x11, x12, y11, y12, x21, x22, y21, y22 } = formatTwoBBox(bbox1, bbox2, format);
4210
+ // bbox1在bbox2内部
3275
4211
  if (x11 > x21 && x12 < x22 && y11 > y21 && y12 < y22) {
3276
4212
  return exports.InnerBBox.BBOX1;
3277
4213
  }
4214
+ // bbox2在bbox1内部
3278
4215
  if (x21 > x11 && x22 < x12 && y21 > y11 && y22 < y12) {
3279
4216
  return exports.InnerBBox.BBOX2;
3280
4217
  }
3281
4218
  return exports.InnerBBox.NONE;
3282
4219
  }
4220
+ /**
4221
+ * 两个矩形是否相交
4222
+ * 如果有矩形为null,判断为相交
4223
+ * @param bbox1
4224
+ * @param bbox2
4225
+ * @param format
4226
+ */
3283
4227
  function isRectIntersect(bbox1, bbox2, format) {
3284
4228
  if (bbox1 && bbox2) {
3285
4229
  if (!format) {
@@ -3296,6 +4240,12 @@
3296
4240
  }
3297
4241
  return true;
3298
4242
  }
4243
+ /**
4244
+ * 点在box内部
4245
+ * 如果bbox为null返回true
4246
+ * @param point
4247
+ * @param bbox
4248
+ */
3299
4249
  function pointInRect(point, bbox, format) {
3300
4250
  if (!bbox) {
3301
4251
  return true;
@@ -3315,9 +4265,20 @@
3315
4265
  }
3316
4266
  return point.x >= x11 && point.x <= x12 && point.y >= y11 && point.y <= y12;
3317
4267
  }
4268
+ // 参考https://github.com/francecil/leetcode/issues/1
4269
+ /**
4270
+ * 计算投影半径
4271
+ * @param {Array(Number)} checkAxis 检测轴 [cosθ,sinθ]
4272
+ * @param {Array} axis 目标轴 [x,y]
4273
+ */
3318
4274
  function getProjectionRadius(checkAxis, axis) {
3319
4275
  return Math.abs(axis[0] * checkAxis[0] + axis[1] * checkAxis[1]);
3320
4276
  }
4277
+ /**
4278
+ * 逆时针旋转矩阵(从正 x 轴开始,旋转到正 y 轴的方向)
4279
+ * [ cos(θ) -sin(θ) ]
4280
+ * [ sin(θ) cos(θ) ]
4281
+ */
3321
4282
  function rotatePoint({ x, y }, rad, origin = { x: 0, y: 0 }) {
3322
4283
  return {
3323
4284
  x: (x - origin.x) * Math.cos(rad) - (y - origin.y) * Math.sin(rad) + origin.x,
@@ -3330,6 +4291,10 @@
3330
4291
  y: (box.y1 + box.y2) / 2
3331
4292
  };
3332
4293
  }
4294
+ /**
4295
+ * 转化为顶点坐标数组
4296
+ * @param {Object} box
4297
+ */
3333
4298
  function toRect(box, isDeg) {
3334
4299
  const deg = isDeg ? degreeToRadian(box.angle) : box.angle;
3335
4300
  const cp = getCenterPoint(box);
@@ -3358,15 +4323,21 @@
3358
4323
  const vector = (start, end) => {
3359
4324
  return [end.x - start.x, end.y - start.y];
3360
4325
  };
4326
+ // 两个矩形的中心点
3361
4327
  const p1 = getCenterPoint(box1);
3362
4328
  const p2 = getCenterPoint(box2);
4329
+ // 向量 p1p2
3363
4330
  const vp1p2 = vector(p1, p2);
4331
+ //矩形1的两边向量
3364
4332
  const AB = vector(rect1[0], rect1[1]);
3365
4333
  const BC = vector(rect1[1], rect1[2]);
4334
+ //矩形2的两边向量
3366
4335
  const A1B1 = vector(rect2[0], rect2[1]);
3367
4336
  const B1C1 = vector(rect2[1], rect2[2]);
4337
+ // 矩形1 的两个弧度
3368
4338
  const deg11 = isDeg ? degreeToRadian(box1.angle) : box1.angle;
3369
4339
  let deg12 = isDeg ? degreeToRadian(90 - box1.angle) : box1.angle + halfPi;
4340
+ // 矩形2 的两个弧度
3370
4341
  const deg21 = isDeg ? degreeToRadian(box2.angle) : box2.angle;
3371
4342
  let deg22 = isDeg ? degreeToRadian(90 - box2.angle) : box2.angle + halfPi;
3372
4343
  if (deg12 > pi2) {
@@ -3375,10 +4346,12 @@
3375
4346
  if (deg22 > pi2) {
3376
4347
  deg22 -= pi2;
3377
4348
  }
4349
+ // 投影重叠
3378
4350
  const isCover = (checkAxisRadius, deg, targetAxis1, targetAxis2) => {
3379
4351
  const checkAxis = [Math.cos(deg), Math.sin(deg)];
3380
4352
  const targetAxisRadius = (getProjectionRadius(checkAxis, targetAxis1) + getProjectionRadius(checkAxis, targetAxis2)) / 2;
3381
4353
  const centerPointRadius = getProjectionRadius(checkAxis, vp1p2);
4354
+ // console.log('abc', `checkAxis:${checkAxis},三个投影:${checkAxisRadius}, ${targetAxisRadius}, ${centerPointRadius}`)
3382
4355
  return checkAxisRadius + targetAxisRadius > centerPointRadius;
3383
4356
  };
3384
4357
  return (isCover((box1.x2 - box1.x1) / 2, deg11, A1B1, B1C1) &&
@@ -3393,6 +4366,7 @@
3393
4366
  let y2;
3394
4367
  function getAABBFromPoints(points) {
3395
4368
  (x1 = Infinity), (y1 = Infinity), (x2 = -Infinity), (y2 = -Infinity);
4369
+ // todo 有优化空间
3396
4370
  points.forEach(point => {
3397
4371
  if (x1 > point.x) {
3398
4372
  x1 = point.x;
@@ -3444,6 +4418,7 @@
3444
4418
  y22 = temp;
3445
4419
  }
3446
4420
  }
4421
+ // 不相交直接跳过
3447
4422
  if (x11 >= x22 || x12 <= x21 || y11 >= y22 || y12 <= y21) {
3448
4423
  return [bounds1, bounds2];
3449
4424
  }
@@ -3465,6 +4440,7 @@
3465
4440
  const l = [];
3466
4441
  list.forEach(b => {
3467
4442
  let arr;
4443
+ // 相交
3468
4444
  if ((arr = unionAABB(baseBound, b)).length > 1) {
3469
4445
  l.push(b);
3470
4446
  return;
@@ -3578,8 +4554,10 @@
3578
4554
  };
3579
4555
  }
3580
4556
  estimate({ text, fontSize }) {
3581
- let eCharLen = 0;
3582
- let cCharLen = 0;
4557
+ // 假设只有英文和中文字符
4558
+ let eCharLen = 0; // 英文字符
4559
+ let cCharLen = 0; // 中文字符
4560
+ // 判断ascii码,如果是
3583
4561
  for (let i = 0; i < text.length; i++) {
3584
4562
  text.charCodeAt(i) < 128 ? eCharLen++ : cCharLen++;
3585
4563
  }
@@ -3597,6 +4575,14 @@
3597
4575
  }
3598
4576
 
3599
4577
  const EPSILON = 1e-8;
4578
+ // Adapted from https://github.com/apache/echarts/blob/master/src/util/graphic.ts by striezel
4579
+ // License: https://github.com/apache/echarts/blob/master/LICENSE
4580
+ /**
4581
+ * Return `true` if the given line (line `a`) and the given polygon
4582
+ * are intersect.
4583
+ * Note that we do not count colinear as intersect here because no
4584
+ * requirement for that. We could do that if required in future.
4585
+ */
3600
4586
  function lineIntersectPolygon(a1x, a1y, a2x, a2y, points) {
3601
4587
  for (let i = 0, p2 = points[points.length - 1]; i < points.length; i++) {
3602
4588
  const p = points[i];
@@ -3607,6 +4593,8 @@
3607
4593
  }
3608
4594
  return false;
3609
4595
  }
4596
+ // Adapted from https://github.com/ecomfe/zrender/blob/master/src/contain/polygon.ts by pissang
4597
+ // License: https://github.com/ecomfe/zrender/blob/master/LICENSE
3610
4598
  function polygonContainPoint(points, x, y) {
3611
4599
  let w = 0;
3612
4600
  let p = points[0];
@@ -3618,30 +4606,44 @@
3618
4606
  w += isPointInLine(p.x, p.y, p2.x, p2.y, x, y);
3619
4607
  p = p2;
3620
4608
  }
4609
+ // Close polygon
3621
4610
  const p0 = points[0];
3622
4611
  if (!isAroundEqual(p.x, p0.x) || !isAroundEqual(p.y, p0.y)) {
3623
4612
  w += isPointInLine(p.x, p.y, p0.x, p0.y, x, y);
3624
4613
  }
3625
4614
  return w !== 0;
3626
4615
  }
4616
+ // Adapted from https://github.com/ecomfe/zrender/blob/master/src/contain/windingLine.ts by pissang
4617
+ // License: https://github.com/ecomfe/zrender/blob/master/LICENSE
3627
4618
  function isPointInLine(x0, y0, x1, y1, x, y) {
3628
4619
  if ((y > y0 && y > y1) || (y < y0 && y < y1)) {
3629
4620
  return 0;
3630
4621
  }
4622
+ // Ignore horizontal line
3631
4623
  if (y1 === y0) {
3632
4624
  return 0;
3633
4625
  }
3634
4626
  const t = (y - y0) / (y1 - y0);
3635
4627
  let dir = y1 < y0 ? 1 : -1;
4628
+ // Avoid winding error when intersection point is the connect point of two line of polygon
3636
4629
  if (t === 1 || t === 0) {
3637
4630
  dir = y1 < y0 ? 0.5 : -0.5;
3638
4631
  }
3639
4632
  const x_ = t * (x1 - x0) + x0;
4633
+ // If (x, y) on the line, considered as "contain".
3640
4634
  return x_ === x ? Infinity : x_ > x ? dir : 0;
3641
4635
  }
4636
+ // Adapted from https://github.com/ecomfe/zrender/blob/master/src/contain/polygon.ts by pissang
4637
+ // License: https://github.com/ecomfe/zrender/blob/master/LICENSE
3642
4638
  function isAroundEqual(a, b) {
3643
4639
  return Math.abs(a - b) < EPSILON;
3644
4640
  }
4641
+ /**
4642
+ * polygon图元之间是否相交
4643
+ * @param pointsA 图元A的points
4644
+ * @param pointsB 图元B的points
4645
+ * @returns 是否相交
4646
+ */
3645
4647
  function polygonIntersectPolygon(pointsA, pointsB) {
3646
4648
  for (let i = 0; i < pointsB.length; i++) {
3647
4649
  if (polygonContainPoint(pointsA, pointsB[i].x, pointsB[i].y)) {
@@ -3698,9 +4700,11 @@
3698
4700
  let width = 0;
3699
4701
  for (const character of string) {
3700
4702
  const codePoint = character.codePointAt(0);
4703
+ // Ignore control characters
3701
4704
  if (codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f)) {
3702
4705
  continue;
3703
4706
  }
4707
+ // Ignore combining characters
3704
4708
  if (codePoint >= 0x300 && codePoint <= 0x36f) {
3705
4709
  continue;
3706
4710
  }
@@ -3727,6 +4731,7 @@
3727
4731
  };
3728
4732
  const ansiRegex = ({ onlyFirst = false } = {}) => {
3729
4733
  const pattern = [
4734
+ // eslint-disable-next-line max-len
3730
4735
  '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
3731
4736
  '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))'
3732
4737
  ].join('|');
@@ -3983,9 +4988,12 @@
3983
4988
  return 'N';
3984
4989
  };
3985
4990
  const emojiRegex = () => {
4991
+ // https://mths.be/emoji
4992
+ // eslint-disable-next-line max-len
3986
4993
  return /[#*0-9]\uFE0F?\u20E3|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26AA\u26B0\u26B1\u26BD\u26BE\u26C4\u26C8\u26CF\u26D1\u26D3\u26E9\u26F0-\u26F5\u26F7\u26F8\u26FA\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B55\u3030\u303D\u3297\u3299]\uFE0F?|[\u261D\u270C\u270D](?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?|[\u270A\u270B](?:\uD83C[\uDFFB-\uDFFF])?|[\u23E9-\u23EC\u23F0\u23F3\u25FD\u2693\u26A1\u26AB\u26C5\u26CE\u26D4\u26EA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2795-\u2797\u27B0\u27BF\u2B50]|\u26F9(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|\u2764\uFE0F?(?:\u200D(?:\uD83D\uDD25|\uD83E\uDE79))?|\uD83C(?:[\uDC04\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]\uFE0F?|[\uDF85\uDFC2\uDFC7](?:\uD83C[\uDFFB-\uDFFF])?|[\uDFC3\uDFC4\uDFCA](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDFCB\uDFCC](?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF]|\uDDE6\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF]|\uDDE7\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF]|\uDDE8\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF]|\uDDE9\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF]|\uDDEA\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA]|\uDDEB\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7]|\uDDEC\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE]|\uDDED\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA]|\uDDEE\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9]|\uDDEF\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5]|\uDDF0\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF]|\uDDF1\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE]|\uDDF2\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF]|\uDDF3\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF]|\uDDF4\uD83C\uDDF2|\uDDF5\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE]|\uDDF6\uD83C\uDDE6|\uDDF7\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC]|\uDDF8\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF]|\uDDF9\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF]|\uDDFA\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF]|\uDDFB\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA]|\uDDFC\uD83C[\uDDEB\uDDF8]|\uDDFD\uD83C\uDDF0|\uDDFE\uD83C[\uDDEA\uDDF9]|\uDDFF\uD83C[\uDDE6\uDDF2\uDDFC]|\uDFF3\uFE0F?(?:\u200D(?:\u26A7\uFE0F?|\uD83C\uDF08))?|\uDFF4(?:\u200D\u2620\uFE0F?|\uDB40\uDC67\uDB40\uDC62\uDB40(?:\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDC73\uDB40\uDC63\uDB40\uDC74|\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F)?)|\uD83D(?:[\uDC08\uDC26](?:\u200D\u2B1B)?|[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3]\uFE0F?|[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC](?:\uD83C[\uDFFB-\uDFFF])?|[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD74\uDD90](?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?|[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC25\uDC27-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED7\uDEDC-\uDEDF\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB\uDFF0]|\uDC15(?:\u200D\uD83E\uDDBA)?|\uDC3B(?:\u200D\u2744\uFE0F?)?|\uDC41\uFE0F?(?:\u200D\uD83D\uDDE8\uFE0F?)?|\uDC68(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDC68\uDC69]\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFE])))?))?|\uDC69(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?[\uDC68\uDC69]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?|\uDC69\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?))|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFE])))?))?|\uDC6F(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDD75(?:\uFE0F|\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDE2E(?:\u200D\uD83D\uDCA8)?|\uDE35(?:\u200D\uD83D\uDCAB)?|\uDE36(?:\u200D\uD83C\uDF2B\uFE0F?)?)|\uD83E(?:[\uDD0C\uDD0F\uDD18-\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5\uDEC3-\uDEC5\uDEF0\uDEF2-\uDEF8](?:\uD83C[\uDFFB-\uDFFF])?|[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD4\uDDD6-\uDDDD](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDDDE\uDDDF](?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD0D\uDD0E\uDD10-\uDD17\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCC\uDDD0\uDDE0-\uDDFF\uDE70-\uDE7C\uDE80-\uDE88\uDE90-\uDEBD\uDEBF-\uDEC2\uDECE-\uDEDB\uDEE0-\uDEE8]|\uDD3C(?:\u200D[\u2640\u2642]\uFE0F?|\uD83C[\uDFFB-\uDFFF])?|\uDDD1(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF-\uDDB3\uDDBC\uDDBD]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?))?|\uDEF1(?:\uD83C(?:\uDFFB(?:\u200D\uD83E\uDEF2\uD83C[\uDFFC-\uDFFF])?|\uDFFC(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFD-\uDFFF])?|\uDFFD(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])?|\uDFFE(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFD\uDFFF])?|\uDFFF(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFE])?))?)/g;
3987
4994
  };
3988
4995
 
4996
+ // FIXME: from VRender
3989
4997
  function getContextFont(text, defaultAttr = {}, fontSizeScale) {
3990
4998
  if (!fontSizeScale) {
3991
4999
  fontSizeScale = 1;
@@ -4002,16 +5010,25 @@
4002
5010
 
4003
5011
  class TextMeasure {
4004
5012
  constructor(option, textSpec) {
5013
+ /** 数字大小缓存 */
4005
5014
  this._numberCharSize = null;
5015
+ /** 全角字符大小缓存 */
4006
5016
  this._fullCharSize = null;
5017
+ /** 字母大小缓存 */
4007
5018
  this._letterCharSize = null;
5019
+ /** 特殊字符缓存 */
4008
5020
  this._specialCharSizeMap = {};
5021
+ /** 内置测量 canvas */
4009
5022
  this._canvas = null;
4010
5023
  this._context = null;
4011
5024
  this._contextSaved = false;
5025
+ /** 是否不支持 canvas */
4012
5026
  this._notSupportCanvas = false;
5027
+ /** 是否不支持 vrender */
4013
5028
  this._notSupportVRender = false;
5029
+ /** 文字 spec */
4014
5030
  this._userSpec = {};
5031
+ /** 特殊字符集 */
4015
5032
  this.specialCharSet = '-/: .,@%\'"~';
4016
5033
  this._option = option;
4017
5034
  this._userSpec = textSpec !== null && textSpec !== void 0 ? textSpec : {};
@@ -4023,6 +5040,7 @@
4023
5040
  ? this.fullMeasure.bind(this)
4024
5041
  : this.measureWithNaiveCanvas.bind(this);
4025
5042
  }
5043
+ /** 初始化测量相关上下文 */
4026
5044
  initContext() {
4027
5045
  if (this._notSupportCanvas) {
4028
5046
  return false;
@@ -4036,6 +5054,7 @@
4036
5054
  typeof window.document !== 'undefined' &&
4037
5055
  globalThis &&
4038
5056
  isValid(globalThis.document)) {
5057
+ // 默认创建方法
4039
5058
  this._canvas = globalThis.document.createElement('canvas');
4040
5059
  }
4041
5060
  }
@@ -4076,6 +5095,7 @@
4076
5095
  lineHeight
4077
5096
  };
4078
5097
  }
5098
+ /** 计算文本宽高 */
4079
5099
  measure(text, method) {
4080
5100
  switch (method) {
4081
5101
  case 'vrender':
@@ -4090,12 +5110,13 @@
4090
5110
  return this.quickMeasure(text);
4091
5111
  }
4092
5112
  }
5113
+ /** 精确计算文本宽高 */
4093
5114
  fullMeasure(text) {
4094
5115
  if (isNil(text)) {
4095
5116
  return { width: 0, height: 0 };
4096
5117
  }
4097
5118
  if (isNil(this._option.getTextBounds) || !this._notSupportVRender) {
4098
- return this.measureWithNaiveCanvas(text);
5119
+ return this.measureWithNaiveCanvas(text); // 降级
4099
5120
  }
4100
5121
  const { fontFamily, fontSize, fontWeight, textAlign, textBaseline, ellipsis, limit, lineHeight } = this.textSpec;
4101
5122
  let size;
@@ -4115,17 +5136,18 @@
4115
5136
  }
4116
5137
  catch (e) {
4117
5138
  this._notSupportVRender = true;
4118
- size = this.measureWithNaiveCanvas(text);
5139
+ size = this.measureWithNaiveCanvas(text); // 降级
4119
5140
  }
4120
5141
  return size;
4121
5142
  }
5143
+ /** 通过原生 canvas 精确计算文本宽度,高度为估算 */
4122
5144
  measureWithNaiveCanvas(text) {
4123
5145
  return this._measureReduce(text, this._measureWithNaiveCanvas.bind(this));
4124
5146
  }
4125
5147
  _measureWithNaiveCanvas(text) {
4126
5148
  var _a;
4127
5149
  if (!this.initContext()) {
4128
- return this._quickMeasureWithoutCanvas(text);
5150
+ return this._quickMeasureWithoutCanvas(text); // 降级
4129
5151
  }
4130
5152
  const metrics = this._context.measureText(text);
4131
5153
  const { fontSize, lineHeight } = this.textSpec;
@@ -4136,6 +5158,7 @@
4136
5158
  fontBoundingBoxDescent: metrics.fontBoundingBoxDescent
4137
5159
  };
4138
5160
  }
5161
+ /** 快速估算文本宽高 */
4139
5162
  quickMeasure(text) {
4140
5163
  return this._measureReduce(text, this._quickMeasure.bind(this));
4141
5164
  }
@@ -4146,13 +5169,17 @@
4146
5169
  };
4147
5170
  for (let i = 0; i < text.length; i++) {
4148
5171
  const char = text[i];
5172
+ // 先判断是否特殊字符
4149
5173
  let size = this._measureSpecialChar(char);
5174
+ // 再判断是否数字
4150
5175
  if (isNil(size) && TextMeasure.NUMBERS_CHAR_SET.includes(char)) {
4151
5176
  size = this._measureNumberChar();
4152
5177
  }
5178
+ // 再判断全角字符
4153
5179
  if (isNil(size) && ['F', 'W'].includes(eastAsianCharacterInfo(char))) {
4154
5180
  size = this._measureFullSizeChar();
4155
5181
  }
5182
+ // 最后按半角字符处理
4156
5183
  if (isNil(size)) {
4157
5184
  size = this._measureLetterChar();
4158
5185
  }
@@ -4163,6 +5190,7 @@
4163
5190
  }
4164
5191
  return totalSize;
4165
5192
  }
5193
+ /** 不使用 canvas 来快速估算文本宽高 */
4166
5194
  quickMeasureWithoutCanvas(text) {
4167
5195
  return this._measureReduce(text, this._quickMeasureWithoutCanvas.bind(this));
4168
5196
  }
@@ -4175,6 +5203,7 @@
4175
5203
  const { fontSize, lineHeight } = this.textSpec;
4176
5204
  for (let i = 0; i < text.length; i++) {
4177
5205
  const char = text[i];
5206
+ // 判断全角字符
4178
5207
  const size = ['F', 'W'].includes(eastAsianCharacterInfo(char)) ? 1 : 0.53;
4179
5208
  totalSize.width += size * fontSize;
4180
5209
  }
@@ -4198,11 +5227,12 @@
4198
5227
  }
4199
5228
  return {
4200
5229
  width: textArr.reduce((maxWidth, cur) => Math.max(maxWidth, processor(cur).width), 0),
4201
- height: textArr.length * (((_a = lineHeight) !== null && _a !== void 0 ? _a : fontSize) + 1) + 1
5230
+ height: textArr.length * (((_a = lineHeight) !== null && _a !== void 0 ? _a : fontSize) + 1) + 1 // 经验值
4202
5231
  };
4203
5232
  }
4204
5233
  return processor(text.toString());
4205
5234
  }
5235
+ /** 测量一个数字 */
4206
5236
  _measureNumberChar() {
4207
5237
  if (isNil(this._numberCharSize)) {
4208
5238
  const numberBounds = this._standardMethod(TextMeasure.NUMBERS_CHAR_SET);
@@ -4215,12 +5245,14 @@
4215
5245
  }
4216
5246
  return this._numberCharSize;
4217
5247
  }
5248
+ /** 测量一个全角字符 */
4218
5249
  _measureFullSizeChar() {
4219
5250
  if (isNil(this._fullCharSize)) {
4220
5251
  this._fullCharSize = this._standardMethod(TextMeasure.FULL_SIZE_CHAR);
4221
5252
  }
4222
5253
  return this._fullCharSize;
4223
5254
  }
5255
+ /** 测量一个英文字符 */
4224
5256
  _measureLetterChar() {
4225
5257
  if (isNil(this._letterCharSize)) {
4226
5258
  const alphabetBounds = this._standardMethod(TextMeasure.ALPHABET_CHAR_SET);
@@ -4233,6 +5265,7 @@
4233
5265
  }
4234
5266
  return this._letterCharSize;
4235
5267
  }
5268
+ /** 测量一个特殊字符 */
4236
5269
  _measureSpecialChar(char) {
4237
5270
  if (isValid(this._specialCharSizeMap[char])) {
4238
5271
  return this._specialCharSizeMap[char];
@@ -4256,8 +5289,11 @@
4256
5289
  }
4257
5290
  }
4258
5291
  }
5292
+ /** 英文字母集 */
4259
5293
  TextMeasure.ALPHABET_CHAR_SET = 'abcdefghijklmnopqrstuvwxyz';
5294
+ /** 数字集 */
4260
5295
  TextMeasure.NUMBERS_CHAR_SET = '0123456789';
5296
+ /** 全角字符 */
4261
5297
  TextMeasure.FULL_SIZE_CHAR = '字';
4262
5298
 
4263
5299
  const calculateAnchorOfBounds = (bounds, anchorType) => {
@@ -4313,26 +5349,28 @@
4313
5349
  { x: Math.cos(a.angle), y: Math.sin(a.angle) },
4314
5350
  { x: -Math.sin(a.angle), y: Math.cos(a.angle) },
4315
5351
  { x: Math.cos(b.angle), y: Math.sin(a.angle) },
4316
- { x: -Math.sin(b.angle), y: Math.cos(a.angle) }
5352
+ { x: -Math.sin(b.angle), y: Math.cos(a.angle) } // Rect B's second axis
4317
5353
  ];
5354
+ // calculate the projection range of a rectangle on a given axis
4318
5355
  function getProjectionRange(obb, axisX, axisY) {
4319
5356
  const corners = obb.getRotatedCorners();
4320
5357
  const projections = corners.map(p => p.x * axisX + p.y * axisY);
4321
5358
  return { min: Math.min(...projections), max: Math.max(...projections) };
4322
5359
  }
5360
+ // Calculate distances for all axes
4323
5361
  let maxDistance = 0;
4324
5362
  for (const axis of axes) {
4325
5363
  const rangeA = getProjectionRange(a, axis.x, axis.y);
4326
5364
  const rangeB = getProjectionRange(b, axis.x, axis.y);
4327
5365
  let distance;
4328
5366
  if (rangeA.max < rangeB.min) {
4329
- distance = rangeB.min - rangeA.max;
5367
+ distance = rangeB.min - rangeA.max; // B is to the right of A
4330
5368
  }
4331
5369
  else if (rangeB.max < rangeA.min) {
4332
- distance = rangeA.min - rangeB.max;
5370
+ distance = rangeA.min - rangeB.max; // A is to the right of B
4333
5371
  }
4334
5372
  else {
4335
- distance = 0;
5373
+ distance = 0; // Overlapping
4336
5374
  }
4337
5375
  maxDistance = Math.max(maxDistance, distance);
4338
5376
  }
@@ -4341,6 +5379,7 @@
4341
5379
 
4342
5380
  function transformBoundsWithMatrix(out, bounds, matrix) {
4343
5381
  const { x1, y1, x2, y2 } = bounds;
5382
+ // 如果没有旋转和缩放,那就直接translate
4344
5383
  if (matrix.onlyTranslate()) {
4345
5384
  if (out !== bounds) {
4346
5385
  out.setValue(bounds.x1, bounds.y1, bounds.x2, bounds.y2);
@@ -4355,6 +5394,16 @@
4355
5394
  out.add(matrix.a * x1 + matrix.c * y2 + matrix.e, matrix.b * x1 + matrix.d * y2 + matrix.f);
4356
5395
  return bounds;
4357
5396
  }
5397
+ /**
5398
+ * 依据graphic的属性对bounds进行变换
5399
+ * @param bounds
5400
+ * @param x
5401
+ * @param y
5402
+ * @param scaleX
5403
+ * @param scaleY
5404
+ * @param angle
5405
+ * @param rotateCenter
5406
+ */
4358
5407
  function transformBounds(bounds, x, y, scaleX, scaleY, angle, rotateCenter) {
4359
5408
  if (abs(scaleX) <= epsilon || abs(scaleY) <= epsilon) {
4360
5409
  return;
@@ -4372,6 +5421,37 @@
4372
5421
  }
4373
5422
  bounds.translate(x, y);
4374
5423
  }
5424
+ /**
5425
+ Copyright (c) 2015-2021, University of Washington Interactive Data Lab
5426
+ All rights reserved.
5427
+
5428
+ Redistribution and use in source and binary forms, with or without
5429
+ modification, are permitted provided that the following conditions are met:
5430
+
5431
+ 1. Redistributions of source code must retain the above copyright notice, this
5432
+ list of conditions and the following disclaimer.
5433
+
5434
+ 2. Redistributions in binary form must reproduce the above copyright notice,
5435
+ this list of conditions and the following disclaimer in the documentation
5436
+ and/or other materials provided with the distribution.
5437
+
5438
+ 3. Neither the name of the copyright holder nor the names of its contributors
5439
+ may be used to endorse or promote products derived from this software
5440
+ without specific prior written permission.
5441
+
5442
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
5443
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5444
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
5445
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
5446
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5447
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
5448
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
5449
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
5450
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
5451
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5452
+ */
5453
+ // 基于Vega的Bounds重构
5454
+ // https://github.com/vega/vega/blob/825bfaba6ccfe3306183df22b8c955a07bb30714/packages/vega-scenegraph/src/Bounds.js
4375
5455
  class Bounds {
4376
5456
  constructor(bounds) {
4377
5457
  if (bounds) {
@@ -4478,6 +5558,11 @@
4478
5558
  const p = this.scalePoints(sx, sy, x, y);
4479
5559
  return this.clear().add(p[0], p[1]).add(p[2], p[3]);
4480
5560
  }
5561
+ /**
5562
+ * 并集
5563
+ * @param b
5564
+ * @returns
5565
+ */
4481
5566
  union(b) {
4482
5567
  if (b.x1 < this.x1) {
4483
5568
  this.x1 = b.x1;
@@ -4493,6 +5578,11 @@
4493
5578
  }
4494
5579
  return this;
4495
5580
  }
5581
+ /**
5582
+ * 交集
5583
+ * @param b
5584
+ * @returns
5585
+ */
4496
5586
  intersect(b) {
4497
5587
  if (b.x1 > this.x1) {
4498
5588
  this.x1 = b.x1;
@@ -4508,15 +5598,36 @@
4508
5598
  }
4509
5599
  return this;
4510
5600
  }
5601
+ /**
5602
+ * 是否包含b
5603
+ * @param b
5604
+ * @returns
5605
+ */
4511
5606
  encloses(b) {
4512
5607
  return b && this.x1 <= b.x1 && this.x2 >= b.x2 && this.y1 <= b.y1 && this.y2 >= b.y2;
4513
5608
  }
5609
+ /**
5610
+ * 是否共边
5611
+ * @param b
5612
+ * @returns
5613
+ */
4514
5614
  alignsWith(b) {
4515
5615
  return b && (this.x1 === b.x1 || this.x2 === b.x2 || this.y1 === b.y1 || this.y2 === b.y2);
4516
5616
  }
5617
+ /**
5618
+ * 是否相交
5619
+ * @param b
5620
+ * @returns
5621
+ */
4517
5622
  intersects(b) {
4518
5623
  return b && !(this.x2 < b.x1 || this.x1 > b.x2 || this.y2 < b.y1 || this.y1 > b.y2);
4519
5624
  }
5625
+ /**
5626
+ * 是否包含
5627
+ * @param x
5628
+ * @param y
5629
+ * @returns
5630
+ */
4520
5631
  contains(x = 0, y = 0) {
4521
5632
  return !(x < this.x1 || x > this.x2 || y < this.y1 || y > this.y2);
4522
5633
  }
@@ -4622,6 +5733,7 @@
4622
5733
  this.f = f;
4623
5734
  }
4624
5735
  equalToMatrix(m2) {
5736
+ // ef -> ad -> bc
4625
5737
  return !(this.e !== m2.e ||
4626
5738
  this.f !== m2.f ||
4627
5739
  this.a !== m2.a ||
@@ -4630,6 +5742,7 @@
4630
5742
  this.c !== m2.c);
4631
5743
  }
4632
5744
  equalTo(a, b, c, d, e, f) {
5745
+ // ef -> ad -> bc
4633
5746
  return !(this.e !== e || this.f !== f || this.a !== a || this.d !== d || this.b !== b || this.c !== c);
4634
5747
  }
4635
5748
  setValue(a, b, c, d, e, f) {
@@ -4650,6 +5763,9 @@
4650
5763
  this.f = 0;
4651
5764
  return this;
4652
5765
  }
5766
+ /**
5767
+ * 获取当前矩阵的逆矩阵
5768
+ */
4653
5769
  getInverse() {
4654
5770
  const a = this.a;
4655
5771
  const b = this.b;
@@ -4681,6 +5797,17 @@
4681
5797
  return this;
4682
5798
  }
4683
5799
  rotateByCenter(rad, cx, cy) {
5800
+ /** rotate matrix
5801
+ * | cos -sin (1-cos)*cx+sin*cy |
5802
+ * | sin cos (1-cos)*cy-sin*cx |
5803
+ * | 0 0 1 |
5804
+ * after multiply
5805
+ * m13 = (1-cos)*cx+sin*cy;
5806
+ * m23 = (1-cos)*cy-sin*cx;
5807
+ * | cos*a-sin*b cos*c-sin*d cos*e-sin*f+m13 |
5808
+ * | sin*a+cos*b sin*c+cos*d sin*e+cos*f+m23 |
5809
+ * | 0 0 1 |
5810
+ */
4684
5811
  const cos = Math.cos(rad);
4685
5812
  const sin = Math.sin(rad);
4686
5813
  const rotateM13 = (1 - cos) * cx + sin * cy;
@@ -4723,6 +5850,15 @@
4723
5850
  return this;
4724
5851
  }
4725
5852
  transpose() {
5853
+ /** transpose matrix
5854
+ * | 0 1 0 |
5855
+ * | 1 0 0 |
5856
+ * | 0 0 1 |
5857
+ * after multiply
5858
+ * | b d f |
5859
+ * | a c e |
5860
+ * | 0 0 1 |
5861
+ */
4726
5862
  const { a, b, c, d, e, f } = this;
4727
5863
  this.a = b;
4728
5864
  this.b = a;
@@ -4732,6 +5868,10 @@
4732
5868
  this.f = e;
4733
5869
  return this;
4734
5870
  }
5871
+ /**
5872
+ * 矩阵相乘
5873
+ * @param matrix
5874
+ */
4735
5875
  multiply(a2, b2, c2, d2, e2, f2) {
4736
5876
  const a1 = this.a;
4737
5877
  const b1 = this.b;
@@ -4753,6 +5893,11 @@
4753
5893
  this.f = dy;
4754
5894
  return this;
4755
5895
  }
5896
+ /**
5897
+ * 插值计算
5898
+ * @param m2
5899
+ * @param t
5900
+ */
4756
5901
  interpolate(m2, t) {
4757
5902
  const m = new Matrix();
4758
5903
  m.a = this.a + (m2.a - this.a) * t;
@@ -4763,6 +5908,11 @@
4763
5908
  m.f = this.f + (m2.f - this.f) * t;
4764
5909
  return m;
4765
5910
  }
5911
+ /**
5912
+ * 将point转到当前矩阵的坐标空间中
5913
+ * @param source
5914
+ * @param target
5915
+ */
4766
5916
  transformPoint(source, target) {
4767
5917
  const { a, b, c, d, e, f } = this;
4768
5918
  const dt = a * d - b * c;
@@ -4776,6 +5926,7 @@
4776
5926
  target.x = x * nextA + y * nextC + nextE;
4777
5927
  target.y = x * nextB + y * nextD + nextF;
4778
5928
  }
5929
+ // 只有translate
4779
5930
  onlyTranslate(scale = 1) {
4780
5931
  return this.a === scale && this.b === 0 && this.c === 0 && this.d === scale;
4781
5932
  }
@@ -4799,6 +5950,7 @@
4799
5950
  skewX: 0,
4800
5951
  skewY: 0
4801
5952
  };
5953
+ // Apply the QR-like decomposition.
4802
5954
  if (a !== 0 || b !== 0) {
4803
5955
  const r = Math.sqrt(a * a + b * b);
4804
5956
  result.rotateDeg = b > 0 ? Math.acos(a / r) : -Math.acos(a / r);
@@ -4820,6 +5972,17 @@
4820
5972
  return result;
4821
5973
  }
4822
5974
  }
5975
+ /**
5976
+ * 对matrix进行通常的变换(基于rotateCenter进行旋转的同时进行translate和scale)
5977
+ * @param out
5978
+ * @param origin
5979
+ * @param x
5980
+ * @param y
5981
+ * @param scaleX
5982
+ * @param scaleY
5983
+ * @param angle
5984
+ * @param rotateCenter
5985
+ */
4823
5986
  function normalTransform(out, origin, x, y, scaleX, scaleY, angle, rotateCenter) {
4824
5987
  const oa = origin.a;
4825
5988
  const ob = origin.b;
@@ -4853,18 +6016,20 @@
4853
6016
  out.f = of + ob * rotateCenterX + od * rotateCenterY - b1 * offsetX - d1 * offsetY;
4854
6017
  }
4855
6018
 
6019
+ // LRU清除
4856
6020
  class LRU {
4857
6021
  constructor() {
4858
6022
  this.CLEAN_THRESHOLD = 1e3;
4859
- this.L_TIME = 1000;
4860
- this.R_COUNT = 1;
4861
- this.R_TIMESTAMP_MAX_SIZE = 20;
6023
+ this.L_TIME = 1000; // 1000ms以内
6024
+ this.R_COUNT = 1; // 使用次数为0
6025
+ this.R_TIMESTAMP_MAX_SIZE = 20; // timestamp最大长度
4862
6026
  }
4863
6027
  clearCache(cache, params) {
4864
6028
  const { CLEAN_THRESHOLD = this.CLEAN_THRESHOLD, L_TIME = this.L_TIME, R_COUNT = this.R_COUNT } = params;
4865
6029
  if (cache.size < CLEAN_THRESHOLD) {
4866
6030
  return 0;
4867
6031
  }
6032
+ // 开始清理,清理最近1000ms内使用次数为0的缓存
4868
6033
  let clearNum = 0;
4869
6034
  const clear = (key) => {
4870
6035
  clearNum++;
@@ -4874,7 +6039,7 @@
4874
6039
  cache.forEach((item, key) => {
4875
6040
  if (item.timestamp.length < R_COUNT) {
4876
6041
  return clear(key);
4877
- }
6042
+ } // 最近使用次数小于R_COUNT,清除
4878
6043
  let useCount = 0;
4879
6044
  while (now - item.timestamp[item.timestamp.length - 1 - useCount] < L_TIME) {
4880
6045
  useCount++;
@@ -4884,7 +6049,8 @@
4884
6049
  }
4885
6050
  if (useCount < R_COUNT) {
4886
6051
  return clear(key);
4887
- }
6052
+ } // 最近L_TIME以内使用次数小于R_COUNT,清除
6053
+ // 清除L_TIME以外的key,避免数组过长
4888
6054
  while (now - item.timestamp[0] > L_TIME) {
4889
6055
  item.timestamp.shift();
4890
6056
  }
@@ -4895,6 +6061,7 @@
4895
6061
  addLimitedTimestamp(cacheItem, t, params) {
4896
6062
  const { R_TIMESTAMP_MAX_SIZE = this.R_TIMESTAMP_MAX_SIZE } = params;
4897
6063
  if (cacheItem.timestamp.length > R_TIMESTAMP_MAX_SIZE) {
6064
+ // cacheItem.timestamp[cacheItem.timestamp.length-1] =
4898
6065
  cacheItem.timestamp.shift();
4899
6066
  }
4900
6067
  cacheItem.timestamp.push(t);
@@ -4966,6 +6133,7 @@
4966
6133
  r /= 255;
4967
6134
  g /= 255;
4968
6135
  b /= 255;
6136
+ // Find greatest and smallest channel values
4969
6137
  const cMin = Math.min(r, g, b);
4970
6138
  const cMax = Math.max(r, g, b);
4971
6139
  const delta = cMax - cMin;
@@ -4975,12 +6143,15 @@
4975
6143
  if (delta === 0) {
4976
6144
  h = 0;
4977
6145
  }
6146
+ // Red is max
4978
6147
  else if (cMax === r) {
4979
6148
  h = ((g - b) / delta) % 6;
4980
6149
  }
6150
+ // Green is max
4981
6151
  else if (cMax === g) {
4982
6152
  h = (b - r) / delta + 2;
4983
6153
  }
6154
+ // Blue is max
4984
6155
  else {
4985
6156
  h = (r - g) / delta + 4;
4986
6157
  }
@@ -4989,7 +6160,9 @@
4989
6160
  h += 360;
4990
6161
  }
4991
6162
  l = (cMax + cMin) / 2;
6163
+ // Calculate saturation
4992
6164
  s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
6165
+ // Multiply l and s by 100
4993
6166
  s = +(s * 100).toFixed(1);
4994
6167
  l = +(l * 100).toFixed(1);
4995
6168
  return {
@@ -4999,6 +6172,34 @@
4999
6172
  };
5000
6173
  }
5001
6174
 
6175
+ /*
6176
+ Copyright (c) 2013, Jason Davies.
6177
+ All rights reserved.
6178
+
6179
+ Redistribution and use in source and binary forms, with or without
6180
+ modification, are permitted provided that the following conditions are met:
6181
+
6182
+ * Redistributions of source code must retain the above copyright notice, this
6183
+ list of conditions and the following disclaimer.
6184
+
6185
+ * Redistributions in binary form must reproduce the above copyright notice,
6186
+ this list of conditions and the following disclaimer in the documentation
6187
+ and/or other materials provided with the distribution.
6188
+
6189
+ * The name Jason Davies may not be used to endorse or promote products
6190
+ derived from this software without specific prior written permission.
6191
+
6192
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
6193
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
6194
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
6195
+ DISCLAIMED. IN NO EVENT SHALL JASON DAVIES BE LIABLE FOR ANY DIRECT, INDIRECT,
6196
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
6197
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
6198
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
6199
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
6200
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
6201
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
6202
+ */
5002
6203
  const REG_HEX = /^#([0-9a-f]{3,8})$/;
5003
6204
  const DEFAULT_COLORS_OPACITY = {
5004
6205
  transparent: 0xffffff00
@@ -5164,6 +6365,7 @@
5164
6365
  else if (isArray(value)) {
5165
6366
  return new RGB(value[0], value[1], value[2]);
5166
6367
  }
6368
+ // default return
5167
6369
  return new RGB(255, 255, 255);
5168
6370
  }
5169
6371
  function rgba(value) {
@@ -5173,6 +6375,7 @@
5173
6375
  else if (isArray(value)) {
5174
6376
  return new RGB(value[0], value[1], value[2], value[3]);
5175
6377
  }
6378
+ // default return
5176
6379
  return new RGB(255, 255, 255, 1);
5177
6380
  }
5178
6381
  function SRGBToLinear(c) {
@@ -5186,12 +6389,15 @@
5186
6389
  if (forceHex || isHex) {
5187
6390
  const hex = parseInt(isHex[1], 16);
5188
6391
  const hexLength = isHex[1].length;
6392
+ // #fff
5189
6393
  if (hexLength === 3) {
5190
6394
  return new RGB(((hex >> 8) & 0xf) + (((hex >> 8) & 0xf) << 4), ((hex >> 4) & 0xf) + (((hex >> 4) & 0xf) << 4), (hex & 0xf) + ((hex & 0xf) << 4), 1);
5191
6395
  }
6396
+ // #ffffff
5192
6397
  if (hexLength === 6) {
5193
6398
  return rgb(hex);
5194
6399
  }
6400
+ // #ffffffaa
5195
6401
  else if (hexLength === 8) {
5196
6402
  return new RGB((hex >> 24) & 0xff, (hex >> 16) & 0xff, (hex >> 8) & 0xff, (hex & 0xff) / 0xff);
5197
6403
  }
@@ -5232,21 +6438,26 @@
5232
6438
  }
5233
6439
  }
5234
6440
  static parseColorString(value) {
6441
+ // case0: transparent
5235
6442
  if (isValid(DEFAULT_COLORS_OPACITY[value])) {
5236
6443
  return rgba(DEFAULT_COLORS_OPACITY[value]);
5237
6444
  }
6445
+ // case1: 简写 ['blue','red']
5238
6446
  if (isValid(DEFAULT_COLORS[value])) {
5239
6447
  return rgb(DEFAULT_COLORS[value]);
5240
6448
  }
5241
6449
  const formatValue = `${value}`.trim().toLowerCase();
6450
+ // case2: 16进制
5242
6451
  const hexRes = setHex(formatValue);
5243
6452
  if (hexRes !== undefined) {
5244
6453
  return hexRes;
5245
6454
  }
6455
+ // case3 : rgb
5246
6456
  if (/^(rgb|RGB|rgba|RGBA)/.test(formatValue)) {
5247
6457
  const aColor = formatValue.replace(/(?:\(|\)|rgba|RGBA|rgb|RGB)*/g, '').split(',');
5248
6458
  return new RGB(parseInt(aColor[0], 10), parseInt(aColor[1], 10), parseInt(aColor[2], 10), parseFloat(aColor[3]));
5249
6459
  }
6460
+ // case4: hsl
5250
6461
  if (/^(hsl|HSL|hsla|HSLA)/.test(formatValue)) {
5251
6462
  const aColor = formatValue.replace(/(?:\(|\)|hsla|HSLA|hsl|HSL)*/g, '').split(',');
5252
6463
  const rgb = hslToRgb(parseInt(aColor[0], 10), parseInt(aColor[1], 10), parseInt(aColor[2], 10));
@@ -5256,10 +6467,12 @@
5256
6467
  }
5257
6468
  constructor(value) {
5258
6469
  const color = Color.parseColorString(value);
6470
+ // case0: transparent
5259
6471
  if (color) {
5260
6472
  this.color = color;
5261
6473
  }
5262
6474
  else {
6475
+ // default: 白色
5263
6476
  console.warn(`Warn: 传入${value}无法解析为Color`);
5264
6477
  this.color = new RGB(255, 255, 255);
5265
6478
  }
@@ -5303,6 +6516,7 @@
5303
6516
  this.color.b = Math.max(0, Math.min(255, Math.floor(b * color.color.b)));
5304
6517
  return this;
5305
6518
  }
6519
+ // todo: 这里亮度计算都没考虑alpha通道
5306
6520
  getHSVBrightness() {
5307
6521
  return Math.max(this.color.r, this.color.g, this.color.b) / 255;
5308
6522
  }
@@ -5330,11 +6544,14 @@
5330
6544
  return res !== null && res !== void 0 ? res : this;
5331
6545
  }
5332
6546
  setColorName(name) {
6547
+ // color keywords
5333
6548
  const hex = DEFAULT_COLORS[name.toLowerCase()];
5334
6549
  if (typeof hex !== 'undefined') {
6550
+ // red
5335
6551
  this.setHex(hex);
5336
6552
  }
5337
6553
  else {
6554
+ // unknown color
5338
6555
  console.warn('THREE.Color: Unknown color ' + name);
5339
6556
  }
5340
6557
  return this;
@@ -5358,6 +6575,9 @@
5358
6575
  getLuminance3() {
5359
6576
  return (0.299 * this.color.r + 0.587 * this.color.g + 0.114 * this.color.b) / 255;
5360
6577
  }
6578
+ /**
6579
+ * https://www.w3.org/TR/WCAG21/#use-of-color
6580
+ */
5361
6581
  getLuminanceWCAG() {
5362
6582
  const RsRGB = this.color.r / 255;
5363
6583
  const GsRGB = this.color.g / 255;
@@ -5443,6 +6663,7 @@
5443
6663
  this.opacity = 1;
5444
6664
  }
5445
6665
  }
6666
+ // 转为16进制
5446
6667
  formatHex() {
5447
6668
  return `#${hex(this.r) + hex(this.g) + hex(this.b) + (this.opacity === 1 ? '' : hex(this.opacity * 255))}`;
5448
6669
  }
@@ -5455,11 +6676,17 @@
5455
6676
  const { h, s, l } = rgbToHsl(this.r, this.g, this.b);
5456
6677
  return `${opacity === 1 ? 'hsl(' : 'hsla('}${h},${s}%,${l}%${opacity === 1 ? ')' : `,${opacity})`}`;
5457
6678
  }
6679
+ // 转为16进制
5458
6680
  toString() {
5459
6681
  return this.formatHex();
5460
6682
  }
5461
6683
  }
5462
6684
 
6685
+ /**
6686
+ * 将 hex 格式颜色转换为 rgb 格式
6687
+ * @param str hex 格式的颜色值
6688
+ * @returns rgb 格式
6689
+ */
5463
6690
  function hexToRgb(str) {
5464
6691
  let r = '';
5465
6692
  let g = '';
@@ -5521,6 +6748,11 @@
5521
6748
  rgbToHsl: rgbToHsl
5522
6749
  });
5523
6750
 
6751
+ /**
6752
+ * 将 padding 转换为通用的格式([上,右,下,左])
6753
+ * @param padding
6754
+ * @return [ top, right, bottom, left ]
6755
+ */
5524
6756
  function normalizePadding(padding) {
5525
6757
  if (isValidNumber(padding)) {
5526
6758
  return [padding, padding, padding, padding];
@@ -5592,6 +6824,12 @@
5592
6824
  function millisecondsSetterName(isUTC) {
5593
6825
  return isUTC ? 'setUTCMilliseconds' : 'setMilliseconds';
5594
6826
  }
6827
+ /**
6828
+ * 根据时间推断格式化字符串
6829
+ * @param value
6830
+ * @param isUTC
6831
+ * @returns
6832
+ */
5595
6833
  function getFormatFromValue(value, isUTC) {
5596
6834
  const date = toDate(value);
5597
6835
  const M = date[monthGetterName(isUTC)]() + 1;
@@ -5639,15 +6877,20 @@
5639
6877
  const m = date[minutesGetterName(isUTC)]();
5640
6878
  const s = date[secondsGetterName(isUTC)]();
5641
6879
  const S = date[millisecondsGetterName(isUTC)]();
6880
+ // 月份,周等需要管理管理locale的时间格式化暂时不支持
5642
6881
  return ((template || '')
5643
6882
  .replace(/YYYY/g, pad(y + '', 4, '0', 'left'))
5644
6883
  .replace(/yyyy/g, y + '')
5645
6884
  .replace(/yy/g, (y % 100) + '')
5646
6885
  .replace(/Q/g, q + '')
6886
+ // .replace(/{MMMM}/g, month[M - 1])
6887
+ // .replace(/{MMM}/g, monthAbbr[M - 1])
5647
6888
  .replace(/MM/g, pad(M, 2, '0', 'left'))
5648
6889
  .replace(/M/g, M + '')
5649
6890
  .replace(/dd/g, pad(d, 2, '0', 'left'))
5650
6891
  .replace(/d/g, d + '')
6892
+ // .replace(/{eeee}/g, dayOfWeek[e])
6893
+ // .replace(/{ee}/g, dayOfWeekAbbr[e])
5651
6894
  .replace(/e/g, e + '')
5652
6895
  .replace(/HH/g, pad(H, 2, '0', 'left'))
5653
6896
  .replace(/H/g, H + '')
@@ -5949,9 +7192,14 @@
5949
7192
  };
5950
7193
  }
5951
7194
  const style = getComputedStyle(el);
7195
+ // clientWidth/clientHeight: 默认整数,会向上取整,导致canvas > container
7196
+ // getBoundingClientRect:默认小数,但是在container上有css类似 transform: scale(0.5)时,获取结果不对
7197
+ // getComputedStyle:默认小数,获取最终结果,但是会包含padding;
5952
7198
  if (/^(\d*\.?\d+)(px)$/.exec(style.width)) {
7199
+ // 当dom元素的 display: 'none' 的时候,获取到的宽高会变成原始的样式配置,而不是计算后的像素值
5953
7200
  const computedWidth = parseFloat(style.width) - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight) || el.clientWidth - 1;
5954
7201
  const computedHeight = parseFloat(style.height) - parseFloat(style.paddingTop) - parseFloat(style.paddingBottom) || el.clientHeight - 1;
7202
+ // 理论上不用向下取整,目前没加。
5955
7203
  return {
5956
7204
  width: computedWidth <= 0 ? defaultWidth : computedWidth,
5957
7205
  height: computedHeight <= 0 ? defaultHeight : computedHeight
@@ -5959,10 +7207,20 @@
5959
7207
  }
5960
7208
  return { width: defaultWidth, height: defaultHeight };
5961
7209
  }
7210
+ /**
7211
+ * 获取元素的绝对位置坐标(相对于页面左上角)
7212
+ * @param element
7213
+ * @returns
7214
+ */
5962
7215
  function getElementAbsolutePosition(element) {
5963
7216
  const { x, y } = element.getBoundingClientRect();
5964
7217
  return { x, y };
5965
7218
  }
7219
+ /**
7220
+ * 获取元素的相对位置坐标(相对于其他dom元素)
7221
+ * @param element
7222
+ * @returns
7223
+ */
5966
7224
  function getElementRelativePosition(element, base) {
5967
7225
  const posElement = getElementAbsolutePosition(element);
5968
7226
  const posBase = getElementAbsolutePosition(base);
@@ -5994,12 +7252,23 @@
5994
7252
  const getScaleY = (element) => {
5995
7253
  return element.getBoundingClientRect().height / element.offsetHeight;
5996
7254
  };
7255
+ /**
7256
+ * 获取目标元素的缩放因数
7257
+ * @param element 目标 dom 元素
7258
+ * @returns
7259
+ */
5997
7260
  const getScale = (element) => {
5998
7261
  if (element.offsetWidth > 0) {
5999
7262
  return getScaleX(element);
6000
7263
  }
6001
7264
  return getScaleY(element);
6002
7265
  };
7266
+ /**
7267
+ * 判断是否是元素的父元素
7268
+ * @param element 从其父元素开始查找的 DOM 元素
7269
+ * @param target 要查找的目标父元素
7270
+ * @returns boolean, true 代表查找到,false 表示未找到
7271
+ */
6003
7272
  function hasParentElement(element, target) {
6004
7273
  let parent = element.parentNode;
6005
7274
  while (parent !== null) {
@@ -6010,6 +7279,11 @@
6010
7279
  }
6011
7280
  return false;
6012
7281
  }
7282
+ /**
7283
+ * 将style字符串转换成对象形式
7284
+ * @param style字符串 例如:"color:red;line-height:20px;"
7285
+ * @returns style对象,如 { color: 'red', 'line-height': '20px' }
7286
+ */
6013
7287
  const styleStringToObject = (styleStr = '') => {
6014
7288
  const res = {};
6015
7289
  styleStr.split(';').forEach(item => {
@@ -6026,9 +7300,19 @@
6026
7300
  });
6027
7301
  return res;
6028
7302
  };
7303
+ /**
7304
+ * 将小驼峰转换成中划线连接的字符串
7305
+ * @param str 如:'lineHeight' => 'line-height'
7306
+ * @returns
7307
+ */
6029
7308
  const lowerCamelCaseToMiddle = (str) => {
6030
7309
  return str.replace(/([A-Z])/g, '-$1').toLowerCase();
6031
7310
  };
7311
+ /**
7312
+ * 将中划线连接的字符串转换成驼峰字符串
7313
+ * @param str 如:'line-height' => 'lineHeight'
7314
+ * @returns
7315
+ */
6032
7316
  function toCamelCase(str) {
6033
7317
  return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
6034
7318
  }
@@ -6037,6 +7321,7 @@
6037
7321
  return obj instanceof Element;
6038
7322
  }
6039
7323
  catch (_a) {
7324
+ // 跨端 plan B
6040
7325
  const htmlElementKeys = [
6041
7326
  'children',
6042
7327
  'innerHTML',
@@ -6104,6 +7389,29 @@
6104
7389
  return distance / factor;
6105
7390
  }
6106
7391
 
7392
+ /**
7393
+ * The MIT License (MIT)
7394
+ *
7395
+ * Copyright (c) 2019 Morgan Herlocker
7396
+ *
7397
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
7398
+ * this software and associated documentation files (the "Software"), to deal in
7399
+ * the Software without restriction, including without limitation the rights to
7400
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7401
+ * the Software, and to permit persons to whom the Software is furnished to do so,
7402
+ * subject to the following conditions:
7403
+ *
7404
+ * The above copyright notice and this permission notice shall be included in allcopies or substantial portions of the Software.
7405
+ *
7406
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
7407
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
7408
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
7409
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
7410
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
7411
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7412
+ *
7413
+ */
7414
+ // https://github.com/Turfjs/turf
6107
7415
  function getGeom(geojson) {
6108
7416
  if (geojson.type === 'Feature') {
6109
7417
  return geojson.geometry;
@@ -6119,6 +7427,7 @@
6119
7427
  }
6120
7428
  const geom = getGeom(polygon);
6121
7429
  const type = geom.type;
7430
+ // BBox [west, south, east, north]
6122
7431
  const bbox = polygon.bbox;
6123
7432
  let polys = geom.coordinates;
6124
7433
  if (bbox && pointInRect(point, { x1: bbox[0], x2: bbox[1], y1: bbox[1], y2: bbox[3] }, true) === true) {
@@ -6139,11 +7448,13 @@
6139
7448
  }
6140
7449
  return result;
6141
7450
  }
7451
+ // https://github.com/Turfjs/turf
6142
7452
  function destination(point, distance, bearing, options = {}) {
6143
7453
  const longitude1 = degreeToRadian(point[0]);
6144
7454
  const latitude1 = degreeToRadian(point[1]);
6145
7455
  const bearingRad = degreeToRadian(bearing);
6146
7456
  const radians = lengthToRadians(distance, options.units);
7457
+ // Main
6147
7458
  const latitude2 = Math.asin(Math.sin(latitude1) * Math.cos(radians) + Math.cos(latitude1) * Math.sin(radians) * Math.cos(bearingRad));
6148
7459
  const longitude2 = longitude1 +
6149
7460
  Math.atan2(Math.sin(bearingRad) * Math.sin(radians) * Math.cos(latitude1), Math.cos(radians) - Math.sin(latitude1) * Math.sin(latitude2));
@@ -6152,17 +7463,41 @@
6152
7463
  return { x: lng, y: lat };
6153
7464
  }
6154
7465
 
7466
+ /* Adapted from venn.js by Ben Frederickson
7467
+ * https://github.com/benfred/venn.js
7468
+ * Licensed under the MIT
7469
+
7470
+ * url: https://github.com/benfred/venn.js/blob/master/src/circleintersection.js
7471
+ * License: https://github.com/benfred/venn.js/blob/master/LICENSE
7472
+ * @license
7473
+ */
6155
7474
  const SMALL = 1e-10;
6156
7475
 
7476
+ /* Adapted from venn.js by Ben Frederickson
7477
+ * https://github.com/benfred/venn.js
7478
+ * Licensed under the MIT
7479
+
7480
+ * url: https://github.com/benfred/venn.js/blob/master/src/circleintersection.js
7481
+ * License: https://github.com/benfred/venn.js/blob/master/LICENSE
7482
+ * @license
7483
+ */
7484
+ /** Returns the intersection area of a bunch of circles (where each circle
7485
+ is an object having an x,y and radius property) */
6157
7486
  function intersectionArea(circles, stats) {
7487
+ // get all the intersection points of the circles
6158
7488
  const intersectionPoints = getIntersectionPoints(circles);
7489
+ // filter out points that aren't included in all the circles
6159
7490
  const innerPoints = intersectionPoints.filter(function (p) {
6160
7491
  return containedInCircles(p, circles);
6161
7492
  });
6162
7493
  let arcArea = 0;
6163
7494
  let polygonArea = 0;
6164
7495
  const arcs = [];
7496
+ // if we have intersection points that are within all the circles,
7497
+ // then figure out the area contained by them
6165
7498
  if (innerPoints.length > 1) {
7499
+ // sort the points by angle from the center of the polygon, which lets
7500
+ // us just iterate over points to get the edges
6166
7501
  const center = getCenter(innerPoints);
6167
7502
  for (let i = 0; i < innerPoints.length; ++i) {
6168
7503
  const p = innerPoints[i];
@@ -6171,14 +7506,20 @@
6171
7506
  innerPoints.sort(function (a, b) {
6172
7507
  return b.angle - a.angle;
6173
7508
  });
7509
+ // iterate over all points, get arc between the points
7510
+ // and update the areas
6174
7511
  let p2 = innerPoints[innerPoints.length - 1];
6175
7512
  for (let i = 0; i < innerPoints.length; ++i) {
6176
7513
  const p1 = innerPoints[i];
7514
+ // polygon area updates easily ...
6177
7515
  polygonArea += (p2.x + p1.x) * (p1.y - p2.y);
7516
+ // updating the arc area is a little more involved
6178
7517
  const midPoint = { x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2 };
6179
7518
  let arc = null;
6180
7519
  for (let j = 0; j < p1.parentIndex.length; ++j) {
6181
7520
  if (p2.parentIndex.indexOf(p1.parentIndex[j]) > -1) {
7521
+ // figure out the angle halfway between the two points
7522
+ // on the current circle
6182
7523
  const circle = circles[p1.parentIndex[j]];
6183
7524
  const a1 = Math.atan2(p1.x - circle.x, p1.y - circle.y);
6184
7525
  const a2 = Math.atan2(p2.x - circle.x, p2.y - circle.y);
@@ -6186,14 +7527,19 @@
6186
7527
  if (angleDiff < 0) {
6187
7528
  angleDiff += 2 * Math.PI;
6188
7529
  }
7530
+ // and use that angle to figure out the width of the
7531
+ // arc
6189
7532
  const a = a2 - angleDiff / 2;
6190
7533
  let width = PointService.distancePP(midPoint, {
6191
7534
  x: circle.x + circle.radius * Math.sin(a),
6192
7535
  y: circle.y + circle.radius * Math.cos(a)
6193
7536
  });
7537
+ // clamp the width to the largest is can actually be
7538
+ // (sometimes slightly overflows because of FP errors)
6194
7539
  if (width > circle.radius * 2) {
6195
7540
  width = circle.radius * 2;
6196
7541
  }
7542
+ // pick the circle whose arc has the smallest width
6197
7543
  if (arc === null || arc.width > width) {
6198
7544
  arc = { circle: circle, width: width, p1: p1, p2: p2 };
6199
7545
  }
@@ -6207,12 +7553,16 @@
6207
7553
  }
6208
7554
  }
6209
7555
  else {
7556
+ // no intersection points, is either disjoint - or is completely
7557
+ // overlapped. figure out which by examining the smallest circle
6210
7558
  let smallest = circles[0];
6211
7559
  for (let i = 1; i < circles.length; ++i) {
6212
7560
  if (circles[i].radius < smallest.radius) {
6213
7561
  smallest = circles[i];
6214
7562
  }
6215
7563
  }
7564
+ // make sure the smallest circle is completely contained in all
7565
+ // the other circles
6216
7566
  let disjoint = false;
6217
7567
  for (let i = 0; i < circles.length; ++i) {
6218
7568
  if (PointService.distancePP(circles[i], smallest) > Math.abs(smallest.radius - circles[i].radius)) {
@@ -6244,6 +7594,7 @@
6244
7594
  }
6245
7595
  return arcArea + polygonArea;
6246
7596
  }
7597
+ /** returns whether a point is contained by all of a list of circles */
6247
7598
  function containedInCircles(point, circles) {
6248
7599
  for (let i = 0; i < circles.length; ++i) {
6249
7600
  if (PointService.distancePP(point, circles[i]) > circles[i].radius + SMALL) {
@@ -6252,6 +7603,7 @@
6252
7603
  }
6253
7604
  return true;
6254
7605
  }
7606
+ /** Gets all intersection points between a bunch of circles */
6255
7607
  function getIntersectionPoints(circles) {
6256
7608
  const ret = [];
6257
7609
  for (let i = 0; i < circles.length; ++i) {
@@ -6266,13 +7618,19 @@
6266
7618
  }
6267
7619
  return ret;
6268
7620
  }
7621
+ /** Circular segment area calculation. See http://mathworld.wolfram.com/CircularSegment.html */
6269
7622
  function circleArea(r, width) {
6270
7623
  return r * r * Math.acos(1 - width / r) - (r - width) * Math.sqrt(width * (2 * r - width));
6271
7624
  }
7625
+ /** Returns the overlap area of two circles of radius r1 and r2 - that
7626
+ have their centers separated by distance d. Simpler faster
7627
+ circle intersection for only two circles */
6272
7628
  function circleOverlap(r1, r2, d) {
7629
+ // no overlap
6273
7630
  if (d >= r1 + r2) {
6274
7631
  return 0;
6275
7632
  }
7633
+ // completely overlapped
6276
7634
  if (d <= Math.abs(r1 - r2)) {
6277
7635
  return Math.PI * Math.min(r1, r2) * Math.min(r1, r2);
6278
7636
  }
@@ -6280,10 +7638,15 @@
6280
7638
  const w2 = r2 - (d * d - r1 * r1 + r2 * r2) / (2 * d);
6281
7639
  return circleArea(r1, w1) + circleArea(r2, w2);
6282
7640
  }
7641
+ /** Given two circles (containing a x/y/radius attributes),
7642
+ returns the intersecting points if possible.
7643
+ note: doesn't handle cases where there are infinitely many
7644
+ intersection points (circles are equivalent):, or only one intersection point*/
6283
7645
  function circleCircleIntersection(p1, p2) {
6284
7646
  const d = PointService.distancePP(p1, p2);
6285
7647
  const r1 = p1.radius;
6286
7648
  const r2 = p2.radius;
7649
+ // if to far away, or self contained - can't be done
6287
7650
  if (d >= r1 + r2 || d <= Math.abs(r1 - r2)) {
6288
7651
  return [];
6289
7652
  }
@@ -6298,6 +7661,7 @@
6298
7661
  { x: x0 - rx, y: y0 + ry }
6299
7662
  ];
6300
7663
  }
7664
+ /** Returns the center of a bunch of points */
6301
7665
  function getCenter(points) {
6302
7666
  const center = { x: 0, y: 0 };
6303
7667
  for (let i = 0; i < points.length; ++i) {
@@ -6309,6 +7673,14 @@
6309
7673
  return center;
6310
7674
  }
6311
7675
 
7676
+ /* Adapted from d3-time-format by Mike Bostock
7677
+ * https://github.com/d3/d3-time-format
7678
+ * Licensed under the ISC
7679
+
7680
+ * url: https://github.com/d3/d3-time-format/blob/d6feb945baa4b7a45898a9ca926ebca044e20657/src/locale.js
7681
+ * License: https://github.com/d3/d3-time-format/blob/main/LICENSE
7682
+ * @license
7683
+ */
6312
7684
  class TimeUtil {
6313
7685
  static getInstance() {
6314
7686
  if (!TimeUtil.instance) {
@@ -6321,8 +7693,9 @@
6321
7693
  this.locale_periods = ['AM', 'PM'];
6322
7694
  this.locale_weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
6323
7695
  this.locale_shortMonths = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
6324
- this.numberRe = /^\s*\d+/;
7696
+ this.numberRe = /^\s*\d+/; // note: ignores next directive
6325
7697
  this.pads = { '-': '', _: ' ', '0': '0' };
7698
+ // const percentRe = /^%/;
6326
7699
  this.requoteRe = /[\\^$*+?|[\]().{}]/g;
6327
7700
  this.locale_months = [
6328
7701
  'January',
@@ -6599,18 +7972,22 @@
6599
7972
  if (i !== string.length) {
6600
7973
  return null;
6601
7974
  }
7975
+ // If a UNIX timestamp is specified, return it.
6602
7976
  if ('Q' in d) {
6603
7977
  return new Date(d.Q);
6604
7978
  }
6605
7979
  if ('s' in d) {
6606
7980
  return new Date(d.s * 1000 + ('L' in d ? d.L : 0));
6607
7981
  }
7982
+ // If this is utcParse, never use the local timezone.
6608
7983
  if (Z && !('Z' in d)) {
6609
7984
  d.Z = 0;
6610
7985
  }
7986
+ // The am-pm flag is 0 for AM, and 1 for PM.
6611
7987
  if ('p' in d) {
6612
7988
  d.H = (d.H % 12) + d.p * 12;
6613
7989
  }
7990
+ // If the month was not specified, inherit from the quarter.
6614
7991
  if (d.m === undefined) {
6615
7992
  d.m = 'q' in d ? d.q : 0;
6616
7993
  }
@@ -6619,6 +7996,7 @@
6619
7996
  d.M += d.Z % 100;
6620
7997
  return that.utcDate(d);
6621
7998
  }
7999
+ // Otherwise, all fields are in local time.
6622
8000
  return that.localDate(d);
6623
8001
  };
6624
8002
  }
@@ -6660,6 +8038,13 @@
6660
8038
  }
6661
8039
  }
6662
8040
 
8041
+ /* Adapted from d3-time-format by Mike Bostock
8042
+ * https://github.com/d3/d3-format
8043
+ * Licensed under the ISC
8044
+
8045
+ * License: https://github.com/d3/d3-format/blob/main/LICENSE
8046
+ * @license
8047
+ */
6663
8048
  function formatDecimal(x) {
6664
8049
  return Math.abs((x = Math.round(x))) >= 1e21 ? x.toLocaleString('en').replace(/,/g, '') : x.toString(10);
6665
8050
  }
@@ -6667,12 +8052,19 @@
6667
8052
  const _x = p ? x.toExponential(p - 1) : x.toExponential();
6668
8053
  const i = _x.indexOf('e');
6669
8054
  if (i < 0) {
6670
- return null;
8055
+ return null; // NaN, ±Infinity
6671
8056
  }
6672
8057
  const coefficient = _x.slice(0, i);
6673
8058
  return [coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient, +_x.slice(i + 1)];
6674
8059
  }
6675
8060
 
8061
+ /* Adapted from d3-time-format by Mike Bostock
8062
+ * https://github.com/d3/d3-format
8063
+ * Licensed under the ISC
8064
+
8065
+ * License: https://github.com/d3/d3-format/blob/main/LICENSE
8066
+ * @license
8067
+ */
6676
8068
  function formatGroup(grouping, thousands) {
6677
8069
  return function (value, width) {
6678
8070
  let i = value.length;
@@ -6694,6 +8086,13 @@
6694
8086
  };
6695
8087
  }
6696
8088
 
8089
+ /* Adapted from d3-time-format by Mike Bostock
8090
+ * https://github.com/d3/d3-format
8091
+ * Licensed under the ISC
8092
+
8093
+ * License: https://github.com/d3/d3-format/blob/main/LICENSE
8094
+ * @license
8095
+ */
6697
8096
  let prefixExponent;
6698
8097
  function formatPrefixAuto(x, p) {
6699
8098
  const d = formatDecimalParts(x, p);
@@ -6710,9 +8109,16 @@
6710
8109
  ? coefficient + new Array(i - n + 1).join('0')
6711
8110
  : i > 0
6712
8111
  ? coefficient.slice(0, i) + '.' + coefficient.slice(i)
6713
- : '0.' + new Array(1 - i).join('0') + formatDecimalParts(x, Math.max(0, p + i - 1))[0];
8112
+ : '0.' + new Array(1 - i).join('0') + formatDecimalParts(x, Math.max(0, p + i - 1))[0]; // less than 1y!
6714
8113
  }
6715
8114
 
8115
+ /* Adapted from d3-time-format by Mike Bostock
8116
+ * https://github.com/d3/d3-format
8117
+ * Licensed under the ISC
8118
+
8119
+ * License: https://github.com/d3/d3-format/blob/main/LICENSE
8120
+ * @license
8121
+ */
6716
8122
  function formatRounded(x, p) {
6717
8123
  const d = formatDecimalParts(x, p);
6718
8124
  if (!d) {
@@ -6727,6 +8133,13 @@
6727
8133
  : coefficient + new Array(exponent - coefficient.length + 2).join('0');
6728
8134
  }
6729
8135
 
8136
+ /* Adapted from d3-time-format by Mike Bostock
8137
+ * https://github.com/d3/d3-format
8138
+ * Licensed under the ISC
8139
+
8140
+ * License: https://github.com/d3/d3-format/blob/main/LICENSE
8141
+ * @license
8142
+ */
6730
8143
  function formatTrim(s) {
6731
8144
  const n = s.length;
6732
8145
  let i0 = -1;
@@ -6802,6 +8215,13 @@
6802
8215
  });
6803
8216
  }
6804
8217
 
8218
+ /* Adapted from d3-time-format by Mike Bostock
8219
+ * https://github.com/d3/d3-format
8220
+ * Licensed under the ISC
8221
+
8222
+ * License: https://github.com/d3/d3-format/blob/main/LICENSE
8223
+ * @license
8224
+ */
6805
8225
  const prefixes = ['y', 'z', 'a', 'f', 'p', 'n', 'µ', 'm', '', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
6806
8226
  class NumberUtil {
6807
8227
  constructor() {
@@ -6850,19 +8270,33 @@
6850
8270
  let precision = specifierIns.precision;
6851
8271
  let trim = specifierIns.trim;
6852
8272
  let type = specifierIns.type;
8273
+ // The "n" type is an alias for ",g".
6853
8274
  if (type === 'n') {
6854
8275
  (comma = true), (type = 'g');
6855
8276
  }
8277
+ // The "" type, and any invalid type, is an alias for ".12~g".
8278
+ // @ts-ignore
6856
8279
  else if (!formatTypes[type]) {
6857
8280
  precision === undefined && (precision = 12), (trim = true), (type = 'g');
6858
8281
  }
8282
+ // If zero fill is specified, padding goes after sign and before digits.
8283
+ // @ts-ignore
6859
8284
  if (zero || (fill === '0' && align === '=')) {
6860
8285
  (zero = true), (fill = '0'), (align = '=');
6861
8286
  }
8287
+ // Compute the prefix and suffix.
8288
+ // For SI-prefix, the suffix is lazily computed.
6862
8289
  const prefix = symbol === '$' ? this.currencyPrefix : symbol === '#' && /[boxX]/.test(type) ? '0' + type.toLowerCase() : '';
6863
8290
  const suffix = symbol === '$' ? this.currencySuffix : /[%p]/.test(type) ? this.percent : '';
8291
+ // What format function should we use?
8292
+ // Is this an integer type?
8293
+ // Can this type generate exponential notation?
6864
8294
  const formatType = formatTypes[type];
6865
8295
  const maybeSuffix = /[defgprstz%]/.test(type);
8296
+ // Set the default precision if not specified,
8297
+ // or clamp the specified precision to the supported range.
8298
+ // For significant precision, it must be in [1, 21].
8299
+ // For fixed precision, it must be in [0, 20].
6866
8300
  precision =
6867
8301
  precision === undefined
6868
8302
  ? 6
@@ -6883,20 +8317,27 @@
6883
8317
  }
6884
8318
  else {
6885
8319
  _value = +_value;
8320
+ // Determine the sign. -0 is not less than 0, but 1 / -0 is!
6886
8321
  let valueNegative = _value < 0 || 1 / _value < 0;
8322
+ // Perform the initial formatting.
6887
8323
  _value = isNaN(_value) ? nan : formatType(Math.abs(_value), precision);
8324
+ // Trim insignificant zeros.
6888
8325
  if (trim) {
6889
8326
  _value = formatTrim(_value);
6890
8327
  }
8328
+ // If a negative value rounds to zero after formatting, and no explicit positive sign is requested, hide the sign.
6891
8329
  if (valueNegative && +_value === 0 && sign !== '+') {
6892
8330
  valueNegative = false;
6893
8331
  }
8332
+ // Compute the prefix and suffix.
6894
8333
  valuePrefix =
6895
8334
  (valueNegative ? (sign === '(' ? sign : minus) : sign === '-' || sign === '(' ? '' : sign) + valuePrefix;
6896
8335
  valueSuffix =
6897
8336
  (type === 's' ? prefixes[8 + prefixExponent / 3] : '') +
6898
8337
  valueSuffix +
6899
8338
  (valueNegative && sign === '(' ? ')' : '');
8339
+ // Break the formatted value into the integer “value” part that can be
8340
+ // grouped, and fractional or exponential “suffix” part that is not.
6900
8341
  if (maybeSuffix) {
6901
8342
  (i = -1), (n = _value.length);
6902
8343
  while (++i < n) {
@@ -6908,15 +8349,19 @@
6908
8349
  }
6909
8350
  }
6910
8351
  }
8352
+ // If the fill character is not "0", grouping is applied before padding.
6911
8353
  if (comma && !zero) {
6912
8354
  _value = group(_value, Infinity);
6913
8355
  }
8356
+ // Compute the padding.
6914
8357
  let length = valuePrefix.length + _value.length + valueSuffix.length;
6915
8358
  let padding = length < width ? new Array(width - length + 1).join(fill) : '';
8359
+ // If the fill character is "0", grouping is applied after padding.
6916
8360
  if (comma && zero) {
6917
8361
  _value = group(padding + _value, padding.length ? width - valueSuffix.length : Infinity);
6918
8362
  padding = '';
6919
8363
  }
8364
+ // Reconstruct the final output based on the desired alignment.
6920
8365
  switch (align) {
6921
8366
  case '<':
6922
8367
  _value = valuePrefix + _value + valueSuffix + padding;
@@ -6970,6 +8415,7 @@
6970
8415
  X: (x) => Math.round(x).toString(16).toUpperCase(),
6971
8416
  x: (x) => Math.round(x).toString(16),
6972
8417
  t: (x, p) => {
8418
+ // 判断是否为整数
6973
8419
  if (Number.isInteger(x)) {
6974
8420
  return x.toFixed(2);
6975
8421
  }
@@ -6987,6 +8433,16 @@
6987
8433
  };
6988
8434
  }
6989
8435
 
8436
+ /* Adapted from fmin by Ben Frederickson
8437
+ * https://github.com/benfred/fmin
8438
+ * Licensed under the BSD-3-Clause
8439
+
8440
+ * url: https://github.com/benfred/fmin/blob/master/src/blas1.js
8441
+ * License: https://github.com/benfred/fmin/blob/master/LICENSE
8442
+ * @license
8443
+ */
8444
+ // need some basic operations on vectors, rather than adding a dependency,
8445
+ // just define here
6990
8446
  function zeros(x) {
6991
8447
  const r = new Array(x);
6992
8448
  for (let i = 0; i < x; ++i) {
@@ -7018,6 +8474,15 @@
7018
8474
  }
7019
8475
  }
7020
8476
 
8477
+ /* Adapted from fmin by Ben Frederickson
8478
+ * https://github.com/benfred/fmin
8479
+ * Licensed under the BSD-3-Clause
8480
+
8481
+ * url: https://github.com/benfred/fmin/blob/master/src/nelderMead.js
8482
+ * License: https://github.com/benfred/fmin/blob/master/LICENSE
8483
+ * @license
8484
+ */
8485
+ /** minimizes a function using the downhill simplex method */
7021
8486
  function nelderMead(f, x0, parameters) {
7022
8487
  parameters = parameters || {};
7023
8488
  const maxIterations = parameters.maxIterations || x0.length * 200;
@@ -7030,6 +8495,7 @@
7030
8495
  const psi = parameters.psi !== undefined ? parameters.psi : -0.5;
7031
8496
  const sigma = parameters.sigma !== undefined ? parameters.sigma : 0.5;
7032
8497
  let maxDiff;
8498
+ // initialize simplex.
7033
8499
  const N = x0.length;
7034
8500
  const simplex = new Array(N + 1);
7035
8501
  simplex[0] = x0;
@@ -7058,6 +8524,8 @@
7058
8524
  for (let iteration = 0; iteration < maxIterations; ++iteration) {
7059
8525
  simplex.sort(sortOrder);
7060
8526
  if (parameters.history) {
8527
+ // copy the simplex (since later iterations will mutate) and
8528
+ // sort it to have a consistent order between iterations
7061
8529
  const sortedSimplex = simplex.map(function (x) {
7062
8530
  const state = x.slice();
7063
8531
  state.fx = x.fx;
@@ -7076,6 +8544,7 @@
7076
8544
  if (Math.abs(simplex[0].fx - simplex[N].fx) < minErrorDelta && maxDiff < minTolerance) {
7077
8545
  break;
7078
8546
  }
8547
+ // compute the centroid of all but the worst point in the simplex
7079
8548
  for (let i = 0; i < N; ++i) {
7080
8549
  centroid[i] = 0;
7081
8550
  for (let j = 0; j < N; ++j) {
@@ -7083,9 +8552,12 @@
7083
8552
  }
7084
8553
  centroid[i] /= N;
7085
8554
  }
8555
+ // reflect the worst point past the centroid and compute loss at reflected
8556
+ // point
7086
8557
  const worst = simplex[N];
7087
8558
  weightedSum(reflected, 1 + rho, centroid, -rho, worst);
7088
8559
  reflected.fx = f(reflected);
8560
+ // if the reflected point is the best seen, then possibly expand
7089
8561
  if (reflected.fx < simplex[0].fx) {
7090
8562
  weightedSum(expanded, 1 + chi, centroid, -chi, worst);
7091
8563
  expanded.fx = f(expanded);
@@ -7096,9 +8568,12 @@
7096
8568
  updateSimplex(reflected);
7097
8569
  }
7098
8570
  }
8571
+ // if the reflected point is worse than the second worst, we need to
8572
+ // contract
7099
8573
  else if (reflected.fx >= simplex[N - 1].fx) {
7100
8574
  let shouldReduce = false;
7101
8575
  if (reflected.fx > worst.fx) {
8576
+ // do an inside contraction
7102
8577
  weightedSum(contracted, 1 + psi, centroid, -psi, worst);
7103
8578
  contracted.fx = f(contracted);
7104
8579
  if (contracted.fx < worst.fx) {
@@ -7109,6 +8584,7 @@
7109
8584
  }
7110
8585
  }
7111
8586
  else {
8587
+ // do an outside contraction
7112
8588
  weightedSum(contracted, 1 - psi * rho, centroid, psi * rho, worst);
7113
8589
  contracted.fx = f(contracted);
7114
8590
  if (contracted.fx < reflected.fx) {
@@ -7119,9 +8595,11 @@
7119
8595
  }
7120
8596
  }
7121
8597
  if (shouldReduce) {
8598
+ // if we don't contract here, we're done
7122
8599
  if (sigma >= 1) {
7123
8600
  break;
7124
8601
  }
8602
+ // do a reduction
7125
8603
  for (let i = 1; i < simplex.length; ++i) {
7126
8604
  weightedSum(simplex[i], 1 - sigma, simplex[0], sigma, simplex[i]);
7127
8605
  simplex[i].fx = f(simplex[i]);
@@ -7136,6 +8614,21 @@
7136
8614
  return { fx: simplex[0].fx, x: simplex[0] };
7137
8615
  }
7138
8616
 
8617
+ /* Adapted from fmin by Ben Frederickson
8618
+ * https://github.com/benfred/fmin
8619
+ * Licensed under the BSD-3-Clause
8620
+
8621
+ * url: https://github.com/benfred/fmin/blob/master/src/linesearch.js
8622
+ * License: https://github.com/benfred/fmin/blob/master/LICENSE
8623
+ * @license
8624
+ */
8625
+ /// searches along line 'pk' for a point that satifies the wolfe conditions
8626
+ /// See 'Numerical Optimization' by Nocedal and Wright p59-60
8627
+ /// f : objective function
8628
+ /// pk : search direction
8629
+ /// current: object containing current gradient/loss
8630
+ /// next: output: contains next gradient/loss
8631
+ /// returns a: step size taken
7139
8632
  function wolfeLineSearch(f, pk, current, next, a, c1, c2) {
7140
8633
  const phi0 = current.fx;
7141
8634
  const phiPrime0 = dotProduct(current.fxprime, pk);
@@ -7188,7 +8681,17 @@
7188
8681
  return a;
7189
8682
  }
7190
8683
 
8684
+ /* Adapted from fmin by Ben Frederickson
8685
+ * https://github.com/benfred/fmin
8686
+ * Licensed under the BSD-3-Clause
8687
+
8688
+ * url: https://github.com/benfred/fmin/blob/master/src/conjugateGradient.js
8689
+ * License: https://github.com/benfred/fmin/blob/master/LICENSE
8690
+ * @license
8691
+ */
7191
8692
  function conjugateGradient(f, initial, params) {
8693
+ // allocate all memory up front here, keep out of the loop for perfomance
8694
+ // reasons
7192
8695
  let current = { x: initial.slice(), fx: 0, fxprime: initial.slice() };
7193
8696
  let next = { x: initial.slice(), fx: 0, fxprime: initial.slice() };
7194
8697
  const yk = initial.slice();
@@ -7201,13 +8704,17 @@
7201
8704
  scale(pk, current.fxprime, -1);
7202
8705
  for (let i = 0; i < maxIterations; ++i) {
7203
8706
  a = wolfeLineSearch(f, pk, current, next, a);
8707
+ // todo: history in wrong spot?
7204
8708
  if (params.history) {
7205
8709
  params.history.push({ x: current.x.slice(), fx: current.fx, fxprime: current.fxprime.slice(), alpha: a });
7206
8710
  }
7207
8711
  if (!a) {
8712
+ // faiiled to find point that satifies wolfe conditions.
8713
+ // reset direction for next iteration
7208
8714
  scale(pk, current.fxprime, -1);
7209
8715
  }
7210
8716
  else {
8717
+ // update direction using Polak–Ribiere CG method
7211
8718
  weightedSum(yk, 1, next.fxprime, -1, current.fxprime);
7212
8719
  const delta_k = dotProduct(current.fxprime, current.fxprime);
7213
8720
  const beta_k = Math.max(0, dotProduct(yk, next.fxprime) / delta_k);
@@ -7270,6 +8777,7 @@
7270
8777
  exports.binaryFuzzySearch = binaryFuzzySearch;
7271
8778
  exports.binaryFuzzySearchInNumberRange = binaryFuzzySearchInNumberRange;
7272
8779
  exports.bisect = bisect;
8780
+ exports.bootstrapEstimate = bootstrapEstimate;
7273
8781
  exports.calculateAnchorOfArc = calculateAnchorOfArc;
7274
8782
  exports.calculateAnchorOfBounds = calculateAnchorOfBounds;
7275
8783
  exports.calculateMaxRadius = calculateMaxRadius;
@@ -7290,6 +8798,7 @@
7290
8798
  exports.conjugateGradient = conjugateGradient;
7291
8799
  exports.constant = constant;
7292
8800
  exports.containedInCircles = containedInCircles;
8801
+ exports.correlation = correlation;
7293
8802
  exports.cos = cos;
7294
8803
  exports.crossProduct = crossProduct;
7295
8804
  exports.crossProductPoint = crossProductPoint;
@@ -7408,6 +8917,7 @@
7408
8917
  exports.lowerFirst = lowerFirst;
7409
8918
  exports.max = max;
7410
8919
  exports.maxInArray = maxInArray;
8920
+ exports.mean = mean;
7411
8921
  exports.median = median;
7412
8922
  exports.memoize = memoize;
7413
8923
  exports.merge = merge;
@@ -7434,6 +8944,7 @@
7434
8944
  exports.monthSetterName = monthSetterName;
7435
8945
  exports.nelderMead = nelderMead;
7436
8946
  exports.norm2 = norm2;
8947
+ exports.normalQuantile = normalQuantile;
7437
8948
  exports.normalTransform = normalTransform;
7438
8949
  exports.normalizeAngle = normalizeAngle;
7439
8950
  exports.normalizePadding = normalizePadding;