@visactor/vutils 1.0.24 → 1.0.26

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 (452) hide show
  1. package/README.zh-CN.md +12 -0
  2. package/REGRESSION.md +36 -0
  3. package/cjs/angle.d.ts +61 -0
  4. package/cjs/angle.js +73 -7
  5. package/cjs/angle.js.map +1 -1
  6. package/cjs/color/Color.d.ts +3 -0
  7. package/cjs/color/Color.js +64 -10
  8. package/cjs/color/Color.js.map +1 -1
  9. package/cjs/color/hexToRgb.d.ts +5 -0
  10. package/cjs/color/hexToRgb.js +5 -0
  11. package/cjs/color/hexToRgb.js.map +1 -1
  12. package/cjs/color/rgbToHsl.js +5 -1
  13. package/cjs/color/rgbToHsl.js.map +1 -1
  14. package/cjs/common/array.d.ts +43 -0
  15. package/cjs/common/array.js +44 -0
  16. package/cjs/common/array.js.map +1 -1
  17. package/cjs/common/bisect.d.ts +21 -0
  18. package/cjs/common/bisect.js +29 -0
  19. package/cjs/common/bisect.js.map +1 -1
  20. package/cjs/common/bootstrap.d.ts +5 -0
  21. package/cjs/common/bootstrap.js +22 -0
  22. package/cjs/common/bootstrap.js.map +1 -0
  23. package/cjs/common/clone.js +6 -0
  24. package/cjs/common/clone.js.map +1 -1
  25. package/cjs/common/cloneDeep.js +4 -1
  26. package/cjs/common/cloneDeep.js.map +1 -1
  27. package/cjs/common/correlation.d.ts +3 -0
  28. package/cjs/common/correlation.js +83 -0
  29. package/cjs/common/correlation.js.map +1 -0
  30. package/cjs/common/debounce.js +22 -4
  31. package/cjs/common/debounce.js.map +1 -1
  32. package/cjs/common/deviation.js +1 -2
  33. package/cjs/common/ecdf.d.ts +8 -0
  34. package/cjs/common/ecdf.js +11 -2
  35. package/cjs/common/ecdf.js.map +1 -1
  36. package/cjs/common/extent.d.ts +4 -0
  37. package/cjs/common/extent.js +4 -2
  38. package/cjs/common/extent.js.map +1 -1
  39. package/cjs/common/field.d.ts +6 -0
  40. package/cjs/common/field.js +3 -0
  41. package/cjs/common/field.js.map +1 -1
  42. package/cjs/common/has.d.ts +17 -0
  43. package/cjs/common/has.js +18 -1
  44. package/cjs/common/has.js.map +1 -1
  45. package/cjs/common/index.d.ts +5 -0
  46. package/cjs/common/index.js +34 -6
  47. package/cjs/common/index.js.map +1 -1
  48. package/cjs/common/interpolate.js +16 -3
  49. package/cjs/common/interpolate.js.map +1 -1
  50. package/cjs/common/isBase64.d.ts +6 -0
  51. package/cjs/common/isBase64.js +6 -0
  52. package/cjs/common/isBase64.js.map +1 -1
  53. package/cjs/common/isBoolean.d.ts +13 -0
  54. package/cjs/common/isBoolean.js +13 -1
  55. package/cjs/common/isBoolean.js.map +1 -1
  56. package/cjs/common/isEmpty.js +11 -1
  57. package/cjs/common/isEmpty.js.map +1 -1
  58. package/cjs/common/isEqual.js +19 -7
  59. package/cjs/common/isEqual.js.map +1 -1
  60. package/cjs/common/isFunction.d.ts +25 -0
  61. package/cjs/common/isFunction.js +27 -2
  62. package/cjs/common/isFunction.js.map +1 -1
  63. package/cjs/common/isNull.js +2 -1
  64. package/cjs/common/isNumber.d.ts +23 -0
  65. package/cjs/common/isNumber.js +23 -1
  66. package/cjs/common/isNumber.js.map +1 -1
  67. package/cjs/common/isNumeric.d.ts +25 -0
  68. package/cjs/common/isNumeric.js +25 -0
  69. package/cjs/common/isNumeric.js.map +1 -1
  70. package/cjs/common/isObject.d.ts +21 -0
  71. package/cjs/common/isObject.js +22 -0
  72. package/cjs/common/isObject.js.map +1 -1
  73. package/cjs/common/isObjectLike.d.ts +20 -0
  74. package/cjs/common/isObjectLike.js +21 -0
  75. package/cjs/common/isObjectLike.js.map +1 -1
  76. package/cjs/common/isPlainObject.d.ts +25 -0
  77. package/cjs/common/isPlainObject.js.map +1 -1
  78. package/cjs/common/isPrototype.js +1 -1
  79. package/cjs/common/isRegExp.js +1 -1
  80. package/cjs/common/isShallowEqual.js +1 -0
  81. package/cjs/common/isShallowEqual.js.map +1 -1
  82. package/cjs/common/isString.d.ts +13 -0
  83. package/cjs/common/isString.js +13 -1
  84. package/cjs/common/isString.js.map +1 -1
  85. package/cjs/common/isType.d.ts +6 -0
  86. package/cjs/common/isType.js +6 -0
  87. package/cjs/common/isType.js.map +1 -1
  88. package/cjs/common/isValidNumber.js.map +1 -1
  89. package/cjs/common/isValidUrl.js.map +1 -1
  90. package/cjs/common/kde.d.ts +28 -0
  91. package/cjs/common/kde.js +29 -5
  92. package/cjs/common/kde.js.map +1 -1
  93. package/cjs/common/mean.d.ts +2 -0
  94. package/cjs/common/mean.js +25 -0
  95. package/cjs/common/mean.js.map +1 -0
  96. package/cjs/common/merge.js +20 -4
  97. package/cjs/common/merge.js.map +1 -1
  98. package/cjs/common/mixin.js +14 -3
  99. package/cjs/common/mixin.js.map +1 -1
  100. package/cjs/common/normalQuantile.d.ts +5 -0
  101. package/cjs/common/normalQuantile.js +21 -0
  102. package/cjs/common/normalQuantile.js.map +1 -0
  103. package/cjs/common/number.d.ts +24 -0
  104. package/cjs/common/number.js +24 -1
  105. package/cjs/common/number.js.map +1 -1
  106. package/cjs/common/pad.d.ts +21 -0
  107. package/cjs/common/pad.js +21 -1
  108. package/cjs/common/pad.js.map +1 -1
  109. package/cjs/common/random.d.ts +3 -0
  110. package/cjs/common/random.js +13 -0
  111. package/cjs/common/random.js.map +1 -1
  112. package/cjs/common/regression-bootstrap.d.ts +14 -0
  113. package/cjs/common/regression-bootstrap.js +37 -0
  114. package/cjs/common/regression-bootstrap.js.map +1 -0
  115. package/cjs/common/regression-linear.d.ts +5 -0
  116. package/cjs/common/regression-linear.js +28 -39
  117. package/cjs/common/regression-linear.js.map +1 -1
  118. package/cjs/common/regression-logistic.d.ts +6 -0
  119. package/cjs/common/regression-logistic.js +25 -14
  120. package/cjs/common/regression-logistic.js.map +1 -1
  121. package/cjs/common/regression-lowess.d.ts +11 -1
  122. package/cjs/common/regression-lowess.js +125 -118
  123. package/cjs/common/regression-lowess.js.map +1 -1
  124. package/cjs/common/regression-polynomial.d.ts +1 -0
  125. package/cjs/common/regression-polynomial.js +24 -94
  126. package/cjs/common/regression-polynomial.js.map +1 -1
  127. package/cjs/common/regression-solver.d.ts +26 -0
  128. package/cjs/common/regression-solver.js +180 -0
  129. package/cjs/common/regression-solver.js.map +1 -0
  130. package/cjs/common/regression-utils.d.ts +13 -1
  131. package/cjs/common/regression-utils.js +27 -12
  132. package/cjs/common/regression-utils.js.map +1 -1
  133. package/cjs/common/throttle.js +3 -0
  134. package/cjs/common/throttle.js.map +1 -1
  135. package/cjs/common/toDate.js +22 -3
  136. package/cjs/common/toDate.js.map +1 -1
  137. package/cjs/common/uuid.d.ts +7 -0
  138. package/cjs/common/uuid.js +15 -1
  139. package/cjs/common/uuid.js.map +1 -1
  140. package/cjs/data-structure/bounds.d.ts +101 -0
  141. package/cjs/data-structure/bounds.js +74 -7
  142. package/cjs/data-structure/bounds.js.map +1 -1
  143. package/cjs/data-structure/hashTable.d.ts +4 -0
  144. package/cjs/data-structure/hashTable.js +11 -2
  145. package/cjs/data-structure/hashTable.js.map +1 -1
  146. package/cjs/data-structure/index.js +6 -2
  147. package/cjs/data-structure/index.js.map +1 -1
  148. package/cjs/data-structure/matrix.d.ts +91 -0
  149. package/cjs/data-structure/matrix.js +52 -4
  150. package/cjs/data-structure/matrix.js.map +1 -1
  151. package/cjs/data-structure/point.d.ts +3 -0
  152. package/cjs/data-structure/point.js +5 -1
  153. package/cjs/data-structure/point.js.map +1 -1
  154. package/cjs/dom.d.ts +36 -0
  155. package/cjs/dom.js +43 -1
  156. package/cjs/dom.js.map +1 -1
  157. package/cjs/fmin/blas1.js +10 -1
  158. package/cjs/fmin/blas1.js.map +1 -1
  159. package/cjs/fmin/conjugate-gradient.js +18 -3
  160. package/cjs/fmin/conjugate-gradient.js.map +1 -1
  161. package/cjs/fmin/linesearch.js +15 -1
  162. package/cjs/fmin/linesearch.js.map +1 -1
  163. package/cjs/fmin/nelder-mead.d.ts +1 -0
  164. package/cjs/fmin/nelder-mead.js +30 -10
  165. package/cjs/fmin/nelder-mead.js.map +1 -1
  166. package/cjs/format/number/formatDecimal.js +9 -1
  167. package/cjs/format/number/formatDecimal.js.map +1 -1
  168. package/cjs/format/number/formatGroup.js +7 -0
  169. package/cjs/format/number/formatGroup.js.map +1 -1
  170. package/cjs/format/number/formatPrefixAuto.js +8 -0
  171. package/cjs/format/number/formatPrefixAuto.js.map +1 -1
  172. package/cjs/format/number/formatRounded.js +7 -0
  173. package/cjs/format/number/formatRounded.js.map +1 -1
  174. package/cjs/format/number/formatTrim.js +7 -0
  175. package/cjs/format/number/formatTrim.js.map +1 -1
  176. package/cjs/format/number/number.js +39 -9
  177. package/cjs/format/number/number.js.map +1 -1
  178. package/cjs/format/time.js +23 -6
  179. package/cjs/format/time.js.map +1 -1
  180. package/cjs/geo/circle-intersection.d.ts +12 -0
  181. package/cjs/geo/circle-intersection.js +54 -9
  182. package/cjs/geo/circle-intersection.js.map +1 -1
  183. package/cjs/geo/constant.js +8 -1
  184. package/cjs/geo/constant.js.map +1 -1
  185. package/cjs/geo/invariant.d.ts +22 -0
  186. package/cjs/geo/invariant.js +24 -1
  187. package/cjs/geo/invariant.js.map +1 -1
  188. package/cjs/graphics/algorithm/aabb.js +7 -3
  189. package/cjs/graphics/algorithm/aabb.js.map +1 -1
  190. package/cjs/graphics/algorithm/intersect.d.ts +48 -0
  191. package/cjs/graphics/algorithm/intersect.js +75 -7
  192. package/cjs/graphics/algorithm/intersect.js.map +1 -1
  193. package/cjs/graphics/bounds-util.js +4 -2
  194. package/cjs/graphics/bounds-util.js.map +1 -1
  195. package/cjs/graphics/graph-util.js +4 -0
  196. package/cjs/graphics/graph-util.js.map +1 -1
  197. package/cjs/graphics/index.js +7 -4
  198. package/cjs/graphics/index.js.map +1 -1
  199. package/cjs/graphics/polygon.d.ts +12 -0
  200. package/cjs/graphics/polygon.js +28 -5
  201. package/cjs/graphics/polygon.js.map +1 -1
  202. package/cjs/graphics/text/measure/interface.d.ts +12 -0
  203. package/cjs/graphics/text/measure/textMeasure.d.ts +23 -0
  204. package/cjs/graphics/text/measure/textMeasure.js +50 -22
  205. package/cjs/graphics/text/measure/textMeasure.js.map +1 -1
  206. package/cjs/graphics/text/measure/util.js +1 -0
  207. package/cjs/graphics/text/measure/util.js.map +1 -1
  208. package/cjs/graphics/text/stringWidth.js +7 -3
  209. package/cjs/graphics/text/stringWidth.js.map +1 -1
  210. package/cjs/index.d.ts +6 -0
  211. package/cjs/index.js +19 -4
  212. package/cjs/index.js.map +1 -1
  213. package/cjs/logger.js +12 -2
  214. package/cjs/logger.js.map +1 -1
  215. package/cjs/lru.js +14 -6
  216. package/cjs/lru.js.map +1 -1
  217. package/cjs/math.d.ts +9 -0
  218. package/cjs/math.js +10 -0
  219. package/cjs/math.js.map +1 -1
  220. package/cjs/padding.d.ts +5 -0
  221. package/cjs/padding.js +5 -0
  222. package/cjs/padding.js.map +1 -1
  223. package/cjs/time/formatUtils.d.ts +6 -0
  224. package/cjs/time/formatUtils.js +7 -0
  225. package/cjs/time/formatUtils.js.map +1 -1
  226. package/dist/index.js +2035 -411
  227. package/dist/index.min.js +128 -1
  228. package/es/angle.d.ts +61 -0
  229. package/es/angle.js +66 -10
  230. package/es/angle.js.map +1 -1
  231. package/es/color/Color.d.ts +3 -0
  232. package/es/color/Color.js +64 -9
  233. package/es/color/Color.js.map +1 -1
  234. package/es/color/hexToRgb.d.ts +5 -0
  235. package/es/color/hexToRgb.js +5 -0
  236. package/es/color/hexToRgb.js.map +1 -1
  237. package/es/color/rgbToHsl.js +5 -1
  238. package/es/color/rgbToHsl.js.map +1 -1
  239. package/es/common/array.d.ts +43 -0
  240. package/es/common/array.js +44 -8
  241. package/es/common/array.js.map +1 -1
  242. package/es/common/bisect.d.ts +21 -0
  243. package/es/common/bisect.js +29 -4
  244. package/es/common/bisect.js.map +1 -1
  245. package/es/common/bootstrap.d.ts +5 -0
  246. package/es/common/bootstrap.js +14 -0
  247. package/es/common/bootstrap.js.map +1 -0
  248. package/es/common/clone.js +6 -0
  249. package/es/common/clone.js.map +1 -1
  250. package/es/common/cloneDeep.js +4 -1
  251. package/es/common/cloneDeep.js.map +1 -1
  252. package/es/common/correlation.d.ts +3 -0
  253. package/es/common/correlation.js +77 -0
  254. package/es/common/correlation.js.map +1 -0
  255. package/es/common/debounce.js +22 -4
  256. package/es/common/debounce.js.map +1 -1
  257. package/es/common/deviation.js +1 -2
  258. package/es/common/ecdf.d.ts +8 -0
  259. package/es/common/ecdf.js +11 -1
  260. package/es/common/ecdf.js.map +1 -1
  261. package/es/common/extent.d.ts +4 -0
  262. package/es/common/extent.js +8 -3
  263. package/es/common/extent.js.map +1 -1
  264. package/es/common/field.d.ts +6 -0
  265. package/es/common/field.js +3 -1
  266. package/es/common/field.js.map +1 -1
  267. package/es/common/has.d.ts +17 -0
  268. package/es/common/has.js +18 -1
  269. package/es/common/has.js.map +1 -1
  270. package/es/common/index.d.ts +5 -0
  271. package/es/common/index.js +24 -0
  272. package/es/common/index.js.map +1 -1
  273. package/es/common/interpolate.js +16 -3
  274. package/es/common/interpolate.js.map +1 -1
  275. package/es/common/isBase64.d.ts +6 -0
  276. package/es/common/isBase64.js +6 -0
  277. package/es/common/isBase64.js.map +1 -1
  278. package/es/common/isBoolean.d.ts +13 -0
  279. package/es/common/isBoolean.js +13 -1
  280. package/es/common/isBoolean.js.map +1 -1
  281. package/es/common/isEmpty.js +11 -1
  282. package/es/common/isEmpty.js.map +1 -1
  283. package/es/common/isEqual.js +19 -7
  284. package/es/common/isEqual.js.map +1 -1
  285. package/es/common/isFunction.d.ts +25 -0
  286. package/es/common/isFunction.js +27 -2
  287. package/es/common/isFunction.js.map +1 -1
  288. package/es/common/isNull.js +2 -1
  289. package/es/common/isNumber.d.ts +23 -0
  290. package/es/common/isNumber.js +23 -1
  291. package/es/common/isNumber.js.map +1 -1
  292. package/es/common/isNumeric.d.ts +25 -0
  293. package/es/common/isNumeric.js +25 -0
  294. package/es/common/isNumeric.js.map +1 -1
  295. package/es/common/isObject.d.ts +21 -0
  296. package/es/common/isObject.js +22 -0
  297. package/es/common/isObject.js.map +1 -1
  298. package/es/common/isObjectLike.d.ts +20 -0
  299. package/es/common/isObjectLike.js +21 -0
  300. package/es/common/isObjectLike.js.map +1 -1
  301. package/es/common/isPlainObject.d.ts +25 -0
  302. package/es/common/isPlainObject.js +26 -0
  303. package/es/common/isPlainObject.js.map +1 -1
  304. package/es/common/isPrototype.js +1 -1
  305. package/es/common/isRegExp.js +1 -1
  306. package/es/common/isShallowEqual.js +1 -0
  307. package/es/common/isShallowEqual.js.map +1 -1
  308. package/es/common/isString.d.ts +13 -0
  309. package/es/common/isString.js +13 -1
  310. package/es/common/isString.js.map +1 -1
  311. package/es/common/isType.d.ts +6 -0
  312. package/es/common/isType.js +6 -0
  313. package/es/common/isType.js.map +1 -1
  314. package/es/common/isValidNumber.js.map +1 -1
  315. package/es/common/isValidUrl.js.map +1 -1
  316. package/es/common/kde.d.ts +28 -0
  317. package/es/common/kde.js +29 -7
  318. package/es/common/kde.js.map +1 -1
  319. package/es/common/mean.d.ts +2 -0
  320. package/es/common/mean.js +19 -0
  321. package/es/common/mean.js.map +1 -0
  322. package/es/common/merge.js +22 -5
  323. package/es/common/merge.js.map +1 -1
  324. package/es/common/mixin.js +14 -3
  325. package/es/common/mixin.js.map +1 -1
  326. package/es/common/normalQuantile.d.ts +5 -0
  327. package/es/common/normalQuantile.js +15 -0
  328. package/es/common/normalQuantile.js.map +1 -0
  329. package/es/common/number.d.ts +24 -0
  330. package/es/common/number.js +24 -2
  331. package/es/common/number.js.map +1 -1
  332. package/es/common/pad.d.ts +21 -0
  333. package/es/common/pad.js +21 -1
  334. package/es/common/pad.js.map +1 -1
  335. package/es/common/random.d.ts +3 -0
  336. package/es/common/random.js +13 -1
  337. package/es/common/random.js.map +1 -1
  338. package/es/common/regression-bootstrap.d.ts +14 -0
  339. package/es/common/regression-bootstrap.js +31 -0
  340. package/es/common/regression-bootstrap.js.map +1 -0
  341. package/es/common/regression-linear.d.ts +5 -0
  342. package/es/common/regression-linear.js +25 -39
  343. package/es/common/regression-linear.js.map +1 -1
  344. package/es/common/regression-logistic.d.ts +6 -0
  345. package/es/common/regression-logistic.js +24 -16
  346. package/es/common/regression-logistic.js.map +1 -1
  347. package/es/common/regression-lowess.d.ts +11 -1
  348. package/es/common/regression-lowess.js +120 -118
  349. package/es/common/regression-lowess.js.map +1 -1
  350. package/es/common/regression-polynomial.d.ts +1 -0
  351. package/es/common/regression-polynomial.js +21 -89
  352. package/es/common/regression-polynomial.js.map +1 -1
  353. package/es/common/regression-solver.d.ts +26 -0
  354. package/es/common/regression-solver.js +168 -0
  355. package/es/common/regression-solver.js.map +1 -0
  356. package/es/common/regression-utils.d.ts +13 -1
  357. package/es/common/regression-utils.js +18 -9
  358. package/es/common/regression-utils.js.map +1 -1
  359. package/es/common/throttle.js +3 -0
  360. package/es/common/throttle.js.map +1 -1
  361. package/es/common/toDate.js +23 -3
  362. package/es/common/toDate.js.map +1 -1
  363. package/es/common/uuid.d.ts +7 -0
  364. package/es/common/uuid.js +15 -1
  365. package/es/common/uuid.js.map +1 -1
  366. package/es/data-structure/bounds.d.ts +101 -0
  367. package/es/data-structure/bounds.js +74 -8
  368. package/es/data-structure/bounds.js.map +1 -1
  369. package/es/data-structure/hashTable.d.ts +4 -0
  370. package/es/data-structure/hashTable.js +11 -2
  371. package/es/data-structure/hashTable.js.map +1 -1
  372. package/es/data-structure/index.js +2 -0
  373. package/es/data-structure/index.js.map +1 -1
  374. package/es/data-structure/matrix.d.ts +91 -0
  375. package/es/data-structure/matrix.js +52 -5
  376. package/es/data-structure/matrix.js.map +1 -1
  377. package/es/data-structure/point.d.ts +3 -0
  378. package/es/data-structure/point.js +5 -1
  379. package/es/data-structure/point.js.map +1 -1
  380. package/es/dom.d.ts +36 -0
  381. package/es/dom.js +43 -8
  382. package/es/dom.js.map +1 -1
  383. package/es/fmin/blas1.js +10 -0
  384. package/es/fmin/blas1.js.map +1 -1
  385. package/es/fmin/conjugate-gradient.js +18 -2
  386. package/es/fmin/conjugate-gradient.js.map +1 -1
  387. package/es/fmin/linesearch.js +15 -0
  388. package/es/fmin/linesearch.js.map +1 -1
  389. package/es/fmin/nelder-mead.d.ts +1 -0
  390. package/es/fmin/nelder-mead.js +29 -9
  391. package/es/fmin/nelder-mead.js.map +1 -1
  392. package/es/format/number/formatDecimal.js +9 -1
  393. package/es/format/number/formatDecimal.js.map +1 -1
  394. package/es/format/number/formatGroup.js +7 -0
  395. package/es/format/number/formatGroup.js.map +1 -1
  396. package/es/format/number/formatPrefixAuto.js +8 -0
  397. package/es/format/number/formatPrefixAuto.js.map +1 -1
  398. package/es/format/number/formatRounded.js +7 -0
  399. package/es/format/number/formatRounded.js.map +1 -1
  400. package/es/format/number/formatTrim.js +7 -0
  401. package/es/format/number/formatTrim.js.map +1 -1
  402. package/es/format/number/number.js +40 -8
  403. package/es/format/number/number.js.map +1 -1
  404. package/es/format/time.js +23 -5
  405. package/es/format/time.js.map +1 -1
  406. package/es/geo/circle-intersection.d.ts +12 -0
  407. package/es/geo/circle-intersection.js +54 -14
  408. package/es/geo/circle-intersection.js.map +1 -1
  409. package/es/geo/constant.js +8 -0
  410. package/es/geo/constant.js.map +1 -1
  411. package/es/geo/invariant.d.ts +22 -0
  412. package/es/geo/invariant.js +24 -0
  413. package/es/geo/invariant.js.map +1 -1
  414. package/es/graphics/algorithm/aabb.js +7 -3
  415. package/es/graphics/algorithm/aabb.js.map +1 -1
  416. package/es/graphics/algorithm/intersect.d.ts +48 -0
  417. package/es/graphics/algorithm/intersect.js +75 -12
  418. package/es/graphics/algorithm/intersect.js.map +1 -1
  419. package/es/graphics/bounds-util.js +4 -2
  420. package/es/graphics/bounds-util.js.map +1 -1
  421. package/es/graphics/graph-util.js +4 -0
  422. package/es/graphics/graph-util.js.map +1 -1
  423. package/es/graphics/index.js +2 -0
  424. package/es/graphics/index.js.map +1 -1
  425. package/es/graphics/polygon.d.ts +12 -0
  426. package/es/graphics/polygon.js +28 -6
  427. package/es/graphics/polygon.js.map +1 -1
  428. package/es/graphics/text/measure/interface.d.ts +12 -0
  429. package/es/graphics/text/measure/textMeasure.d.ts +23 -0
  430. package/es/graphics/text/measure/textMeasure.js +46 -21
  431. package/es/graphics/text/measure/textMeasure.js.map +1 -1
  432. package/es/graphics/text/measure/util.js +1 -0
  433. package/es/graphics/text/measure/util.js.map +1 -1
  434. package/es/graphics/text/stringWidth.js +7 -3
  435. package/es/graphics/text/stringWidth.js.map +1 -1
  436. package/es/index.d.ts +6 -0
  437. package/es/index.js +15 -1
  438. package/es/index.js.map +1 -1
  439. package/es/logger.js +12 -1
  440. package/es/logger.js.map +1 -1
  441. package/es/lru.js +14 -6
  442. package/es/lru.js.map +1 -1
  443. package/es/math.d.ts +9 -0
  444. package/es/math.js +10 -1
  445. package/es/math.js.map +1 -1
  446. package/es/padding.d.ts +5 -0
  447. package/es/padding.js +5 -1
  448. package/es/padding.js.map +1 -1
  449. package/es/time/formatUtils.d.ts +6 -0
  450. package/es/time/formatUtils.js +7 -1
  451. package/es/time/formatUtils.js.map +1 -1
  452. package/package.json +4 -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,233 @@
1716
2236
  return [min, max];
1717
2237
  };
1718
2238
 
1719
- function invNorm(p) {
1720
- if (p <= 0 || p >= 1) {
1721
- return 0;
2239
+ function regressionPoints(data, x, y) {
2240
+ const points = [];
2241
+ data.forEach(d => {
2242
+ const a = x(d);
2243
+ const b = y(d);
2244
+ // 保留既有 accessor 对缺失观测的排除行为;无穷大不是可拟合观测。
2245
+ if (a != null && b != null && Number.isFinite(+a) && Number.isFinite(+b)) {
2246
+ points.push({ x: +a, y: +b });
2247
+ }
2248
+ });
2249
+ return points;
2250
+ }
2251
+ function normalizeRegression(value, center, scale) {
2252
+ const delta = value - center;
2253
+ return Number.isFinite(delta) ? delta / scale : value / scale - center / scale;
2254
+ }
2255
+ function regressionBasis(points) {
2256
+ let xMin = Infinity;
2257
+ let xMax = -Infinity;
2258
+ let yMin = Infinity;
2259
+ let yMax = -Infinity;
2260
+ for (const p of points) {
2261
+ xMin = Math.min(xMin, p.x);
2262
+ xMax = Math.max(xMax, p.x);
2263
+ yMin = Math.min(yMin, p.y);
2264
+ yMax = Math.max(yMax, p.y);
2265
+ }
2266
+ if (!points.length) {
2267
+ return { xCenter: 0, xScale: 1, yCenter: 0, yScale: 1 };
2268
+ }
2269
+ const xCenter = xMin / 2 + xMax / 2;
2270
+ const yCenter = yMin / 2 + yMax / 2;
2271
+ return {
2272
+ xCenter,
2273
+ yCenter,
2274
+ xScale: Math.max(Math.abs(xMin - xCenter), Math.abs(xMax - xCenter)) || 1,
2275
+ yScale: Math.max(Math.abs(yMin - yCenter), Math.abs(yMax - yCenter)) || 1
2276
+ };
2277
+ }
2278
+ /** 秩不足时对 QR 的小型 R 做单边 Jacobi SVD,获得明确的最小范数解。 */
2279
+ function minimumNorm(r, rhs) {
2280
+ const p = rhs.length;
2281
+ const a = r.map(row => row.slice());
2282
+ const v = Array.from({ length: p }, (_, i) => Array.from({ length: p }, (_, j) => +(i === j)));
2283
+ for (let sweep = 0; sweep < 50; sweep++) {
2284
+ let changed = false;
2285
+ for (let j = 0; j < p - 1; j++) {
2286
+ for (let k = j + 1; k < p; k++) {
2287
+ let aa = 0;
2288
+ let bb = 0;
2289
+ let ab = 0;
2290
+ for (let i = 0; i < p; i++) {
2291
+ aa += a[i][j] ** 2;
2292
+ bb += a[i][k] ** 2;
2293
+ ab += a[i][j] * a[i][k];
2294
+ }
2295
+ if (Math.abs(ab) <= Number.EPSILON * 8 * Math.sqrt(aa * bb)) {
2296
+ continue;
2297
+ }
2298
+ changed = true;
2299
+ const tau = (bb - aa) / (2 * ab);
2300
+ const t = (tau >= 0 ? 1 : -1) / (Math.abs(tau) + Math.hypot(1, tau));
2301
+ const c = 1 / Math.hypot(1, t);
2302
+ const s = c * t;
2303
+ for (let i = 0; i < p; i++) {
2304
+ const aj = a[i][j];
2305
+ const ak = a[i][k];
2306
+ const vj = v[i][j];
2307
+ const vk = v[i][k];
2308
+ a[i][j] = c * aj - s * ak;
2309
+ a[i][k] = s * aj + c * ak;
2310
+ v[i][j] = c * vj - s * vk;
2311
+ v[i][k] = s * vj + c * vk;
2312
+ }
2313
+ }
2314
+ }
2315
+ if (!changed) {
2316
+ break;
2317
+ }
1722
2318
  }
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);
2319
+ const norms = Array.from({ length: p }, (_, j) => Math.hypot(...a.map(row => row[j])));
2320
+ const tolerance = Math.max(...norms) * Number.EPSILON * p * 64;
2321
+ const coef = new Array(p).fill(0);
2322
+ let rank = 0;
2323
+ for (let j = 0; j < p; j++) {
2324
+ if (norms[j] <= tolerance) {
2325
+ continue;
2326
+ }
2327
+ rank++;
2328
+ let projection = 0;
2329
+ for (let i = 0; i < p; i++) {
2330
+ projection += a[i][j] * rhs[i];
2331
+ }
2332
+ projection /= norms[j] ** 2;
2333
+ for (let i = 0; i < p; i++) {
2334
+ coef[i] += v[i][j] * projection;
2335
+ }
1751
2336
  }
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));
2337
+ return { coef, rank };
2338
+ }
2339
+ function fitRegression(points, degree, basis = regressionBasis(points)) {
2340
+ const p = degree + 1;
2341
+ if (degree === 1) {
2342
+ let mx = 0;
2343
+ let my = 0;
2344
+ let n = 0;
2345
+ for (const row of points) {
2346
+ n++;
2347
+ mx += (normalizeRegression(row.x, basis.xCenter, basis.xScale) - mx) / n;
2348
+ my += (normalizeRegression(row.y, basis.yCenter, basis.yScale) - my) / n;
2349
+ }
2350
+ let sxx = 0;
2351
+ let sxy = 0;
2352
+ for (const row of points) {
2353
+ const dx = normalizeRegression(row.x, basis.xCenter, basis.xScale) - mx;
2354
+ sxx += dx * dx;
2355
+ sxy += dx * (normalizeRegression(row.y, basis.yCenter, basis.yScale) - my);
2356
+ }
2357
+ const b = sxx === 0 ? 0 : sxy / sxx;
2358
+ const coef = sxx === 0 ? [my / (1 + mx * mx), (mx * my) / (1 + mx * mx)] : [my - b * mx, b];
2359
+ return { coef, rank: n === 0 ? 0 : sxx === 0 ? 1 : 2, basis };
2360
+ }
2361
+ const r = Array.from({ length: p }, () => new Array(p).fill(0));
2362
+ const rhs = new Array(p).fill(0);
2363
+ const row = new Array(p);
2364
+ for (const point of points) {
2365
+ const z = normalizeRegression(point.x, basis.xCenter, basis.xScale);
2366
+ row[0] = 1;
2367
+ for (let j = 1; j < p; j++) {
2368
+ row[j] = row[j - 1] * z;
2369
+ }
2370
+ let value = normalizeRegression(point.y, basis.yCenter, basis.yScale);
2371
+ // 增量 Givens QR 仅保留 p×p 的 R,bootstrap 不分配 N×p 矩阵。
2372
+ for (let j = 0; j < p; j++) {
2373
+ const norm = Math.hypot(r[j][j], row[j]);
2374
+ if (norm === 0) {
2375
+ continue;
2376
+ }
2377
+ const c = r[j][j] / norm;
2378
+ const s = row[j] / norm;
2379
+ r[j][j] = norm;
2380
+ for (let k = j + 1; k < p; k++) {
2381
+ const previous = r[j][k];
2382
+ r[j][k] = c * previous + s * row[k];
2383
+ row[k] = -s * previous + c * row[k];
2384
+ }
2385
+ const previous = rhs[j];
2386
+ rhs[j] = c * previous + s * value;
2387
+ value = -s * previous + c * value;
2388
+ }
2389
+ }
2390
+ const tolerance = Math.max(...r.map((row, i) => Math.abs(row[i]))) * Number.EPSILON * p * 64;
2391
+ if (r.some((row, i) => Math.abs(row[i]) <= tolerance)) {
2392
+ return Object.assign(Object.assign({}, minimumNorm(r, rhs)), { basis });
2393
+ }
2394
+ const coef = new Array(p);
2395
+ for (let i = p - 1; i >= 0; i--) {
2396
+ let value = rhs[i];
2397
+ for (let j = i + 1; j < p; j++) {
2398
+ value -= r[i][j] * coef[j];
2399
+ }
2400
+ coef[i] = value / r[i][i];
2401
+ }
2402
+ return { coef, rank: p, basis };
2403
+ }
2404
+ function regressionPrediction(model, x) {
2405
+ const z = normalizeRegression(x, model.basis.xCenter, model.basis.xScale);
2406
+ let value = 0;
2407
+ for (let j = model.coef.length - 1; j >= 0; j--) {
2408
+ value = value * z + model.coef[j];
2409
+ }
2410
+ return model.basis.yCenter + model.basis.yScale * value;
2411
+ }
2412
+ /** 保留公开的升幂 coef;预测始终使用稳定基底。 */
2413
+ function regressionCoefficients(model) {
2414
+ let coef = [model.coef[model.coef.length - 1]];
2415
+ const a = -model.basis.xCenter / model.basis.xScale;
2416
+ const b = 1 / model.basis.xScale;
2417
+ for (let j = model.coef.length - 2; j >= 0; j--) {
2418
+ const next = new Array(coef.length + 1).fill(0);
2419
+ coef.forEach((c, i) => {
2420
+ next[i] += a * c;
2421
+ next[i + 1] += b * c;
2422
+ });
2423
+ next[0] += model.coef[j];
2424
+ coef = next;
1757
2425
  }
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));
2426
+ coef = coef.map(c => c * model.basis.yScale);
2427
+ coef[0] += model.basis.yCenter;
2428
+ if (coef.some(c => !Number.isFinite(c))) {
2429
+ throw new RangeError('Regression coefficients are not finite');
2430
+ }
2431
+ return coef;
1760
2432
  }
2433
+ function regressionGrid(min, max, count, predict) {
2434
+ if (!Number.isInteger(count) || count <= 0 || !Number.isFinite(min) || !Number.isFinite(max)) {
2435
+ return [];
2436
+ }
2437
+ const width = max - min;
2438
+ const step = count === 1 ? 0 : width / (count - 1);
2439
+ return Array.from({ length: count }, (_, i) => {
2440
+ const t = count === 1 ? 0 : i / (count - 1);
2441
+ const x = i === 0 ? min : i === count - 1 ? max : Number.isFinite(width) ? min + i * step : (1 - t) * min + t * max;
2442
+ return { x, y: predict(x) };
2443
+ });
2444
+ }
2445
+
2446
+ /** Compute a two-sided normal critical value without subtracting a small tail probability from one. */
2447
+ function confidenceCriticalValue(alpha) {
2448
+ if (!Number.isFinite(alpha) || alpha < 0 || alpha > 1) {
2449
+ return NaN;
2450
+ }
2451
+ return -normalQuantile(alpha / 2);
2452
+ }
2453
+ /** A zero standard error gives a degenerate interval even when the critical value is infinite. */
2454
+ function confidenceHalfWidth(z, standardError) {
2455
+ if (Number.isNaN(z)) {
2456
+ return NaN;
2457
+ }
2458
+ return standardError === 0 ? 0 : z * standardError;
2459
+ }
2460
+ /**
2461
+ * Compute basic components used for linear regression confidence / prediction intervals.
2462
+ * - scans data to compute min/max of x, n, mean X, SSE and Sxx (sum (xi - X)^2).
2463
+ */
1761
2464
  function computeLinearCIComponents(data, x, y, predict) {
2465
+ // simple local scanner to avoid circular imports
1762
2466
  let min = Infinity;
1763
2467
  let max = -Infinity;
1764
2468
  let n = 0;
@@ -1797,6 +2501,9 @@
1797
2501
  }
1798
2502
  return { min, max, n, X, SSE, Sxx };
1799
2503
  }
2504
+ /**
2505
+ * Compute standard errors for mean and prediction at px using components.
2506
+ */
1800
2507
  function stdErrorsAt(px, comps) {
1801
2508
  const { n, X, Sxx, SSE } = comps;
1802
2509
  const s2 = n > 2 ? SSE / (n - 2) : 0;
@@ -1805,6 +2512,10 @@
1805
2512
  return { seMean, sePred };
1806
2513
  }
1807
2514
 
2515
+ /**
2516
+ * Linear regression utilities (single clean implementation).
2517
+ * Exports: ordinaryLeastSquares, visitPoints, rSquared, regressionLinear
2518
+ */
1808
2519
  function ordinaryLeastSquares(uX, uY, uXY, uX2) {
1809
2520
  const denom = uX2 - uX * uX;
1810
2521
  if (Math.abs(denom) < Number.EPSILON) {
@@ -1843,56 +2554,39 @@
1843
2554
  function regressionLinear(data, x = d => d.x, y = d => d.y, options) {
1844
2555
  var _a;
1845
2556
  const alpha = (_a = options === null || options === void 0 ? void 0 : options.alpha) !== null && _a !== void 0 ? _a : 0.05;
1846
- let n = 0;
1847
- let meanX = 0;
2557
+ const points = regressionPoints(data, x, y);
2558
+ const model = fitRegression(points, 1);
2559
+ const coef = regressionCoefficients(model);
2560
+ const a = coef[0];
2561
+ const b = coef[1];
2562
+ const predict = (xx) => regressionPrediction(model, xx);
2563
+ const comps = computeLinearCIComponents(data, x, y, predict);
1848
2564
  let meanY = 0;
1849
- let meanXY = 0;
1850
- let meanX2 = 0;
1851
- visitPoints(data, x, y, (xi, yi) => {
1852
- n++;
1853
- meanX += (xi - meanX) / n;
1854
- meanY += (yi - meanY) / n;
1855
- meanXY += (xi * yi - meanXY) / n;
1856
- meanX2 += (xi * xi - meanX2) / n;
2565
+ points.forEach((p, i) => {
2566
+ meanY += (p.y - meanY) / (i + 1);
1857
2567
  });
1858
- const { a, b } = ordinaryLeastSquares(meanX, meanY, meanXY, meanX2);
1859
- const predict = (xx) => a + b * xx;
1860
- const comps = computeLinearCIComponents(data, x, y, predict);
1861
2568
  function evaluateGrid(N) {
1862
- const out = [];
1863
- if (comps.n === 0 || N <= 0) {
1864
- return out;
1865
- }
1866
- if (comps.min === comps.max) {
1867
- for (let i = 0; i < N; i++) {
1868
- out.push({ x: comps.min, y: predict(comps.min) });
1869
- }
1870
- return out;
1871
- }
1872
- const step = (comps.max - comps.min) / (N - 1);
1873
- for (let i = 0; i < N; i++) {
1874
- const px = i === N - 1 ? comps.max : comps.min + step * i;
1875
- out.push({ x: px, y: predict(px) });
1876
- }
1877
- return out;
2569
+ return regressionGrid(comps.min, comps.max, N, predict);
1878
2570
  }
1879
2571
  function confidenceInterval(N = 50) {
1880
2572
  const out = [];
1881
2573
  if (comps.n === 0 || N <= 0) {
1882
2574
  return out;
1883
2575
  }
1884
- const z = invNorm(1 - alpha / 2);
2576
+ const z = confidenceCriticalValue(alpha);
1885
2577
  if (comps.min === comps.max) {
1886
2578
  const m = predict(comps.min);
1887
2579
  const errs = stdErrorsAt(comps.min, comps);
2580
+ const meanHalfWidth = confidenceHalfWidth(z, errs.seMean);
2581
+ const predHalfWidth = confidenceHalfWidth(z, errs.sePred);
1888
2582
  for (let i = 0; i < N; i++) {
1889
2583
  out.push({
1890
2584
  x: comps.min,
1891
2585
  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
2586
+ lower: m - meanHalfWidth,
2587
+ upper: m + meanHalfWidth,
2588
+ predLower: m - predHalfWidth,
2589
+ predUpper: m + predHalfWidth
1896
2590
  });
1897
2591
  }
1898
2592
  return out;
@@ -1902,19 +2596,22 @@
1902
2596
  const px = i === N - 1 ? comps.max : comps.min + step * i;
1903
2597
  const m = predict(px);
1904
2598
  const errs = stdErrorsAt(px, comps);
2599
+ const meanHalfWidth = confidenceHalfWidth(z, errs.seMean);
2600
+ const predHalfWidth = confidenceHalfWidth(z, errs.sePred);
1905
2601
  out.push({
1906
2602
  x: px,
1907
2603
  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
2604
+ lower: m - meanHalfWidth,
2605
+ upper: m + meanHalfWidth,
2606
+ predLower: m - predHalfWidth,
2607
+ predUpper: m + predHalfWidth
1912
2608
  });
1913
2609
  }
1914
2610
  return out;
1915
2611
  }
1916
2612
  return {
1917
2613
  coef: { a, b },
2614
+ rank: model.rank,
1918
2615
  predict,
1919
2616
  rSquared: rSquared(data, x, y, meanY, predict),
1920
2617
  evaluateGrid,
@@ -1922,11 +2619,18 @@
1922
2619
  };
1923
2620
  }
1924
2621
 
2622
+ /**
2623
+ * Simple logistic regression (binary) using Newton-Raphson (IRLS).
2624
+ * Returns { coef, predict, evaluateGrid }
2625
+ * - coef: [intercept, beta]
2626
+ * - predict(x): probability p(y=1|x)
2627
+ */
1925
2628
  function regressionLogistic(data, x = d => d.x, y = d => d.y, options) {
1926
2629
  var _a, _b, _c;
1927
2630
  const maxIter = (_a = options === null || options === void 0 ? void 0 : options.maxIteration) !== null && _a !== void 0 ? _a : 25;
1928
2631
  const tol = (_b = options === null || options === void 0 ? void 0 : options.tol) !== null && _b !== void 0 ? _b : 1e-6;
1929
2632
  const alpha = (_c = options === null || options === void 0 ? void 0 : options.alpha) !== null && _c !== void 0 ? _c : 0.05;
2633
+ // build arrays
1930
2634
  const xs = [];
1931
2635
  const ys = [];
1932
2636
  visitPoints(data, x, y, (dx, dy) => {
@@ -1942,6 +2646,7 @@
1942
2646
  confidenceInterval: (N = 50) => []
1943
2647
  };
1944
2648
  }
2649
+ // initial coef via linear regression rough guess
1945
2650
  let intercept = 0;
1946
2651
  let beta = 0;
1947
2652
  for (let iter = 0; iter < maxIter; iter++) {
@@ -1952,6 +2657,7 @@
1952
2657
  const pi = 1 / (1 + Math.exp(-z));
1953
2658
  p[i] = pi;
1954
2659
  }
2660
+ // compute gradient and hessian
1955
2661
  let g0 = 0;
1956
2662
  let g1 = 0;
1957
2663
  let h00 = 0;
@@ -1966,6 +2672,7 @@
1966
2672
  h01 += wi * xs[i];
1967
2673
  h11 += wi * xs[i] * xs[i];
1968
2674
  }
2675
+ // solve 2x2 system H * delta = g
1969
2676
  const det = h00 * h11 - h01 * h01;
1970
2677
  if (Math.abs(det) < 1e-12) {
1971
2678
  break;
@@ -2026,18 +2733,20 @@
2026
2733
  if (comps.n === 0) {
2027
2734
  return out;
2028
2735
  }
2029
- const z = Math.abs(invNorm(1 - alpha / 2));
2736
+ const z = confidenceCriticalValue(alpha);
2030
2737
  if (comps.min === comps.max) {
2031
2738
  const v = predict(comps.min);
2032
2739
  const errs = stdErrorsAt(comps.min, comps);
2740
+ const meanHalfWidth = confidenceHalfWidth(z, errs.seMean);
2741
+ const predHalfWidth = confidenceHalfWidth(z, errs.sePred);
2033
2742
  for (let i = 0; i < N; i++) {
2034
2743
  out.push({
2035
2744
  x: comps.min,
2036
2745
  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
2746
+ lower: v - meanHalfWidth,
2747
+ upper: v + meanHalfWidth,
2748
+ predLower: v - predHalfWidth,
2749
+ predUpper: v + predHalfWidth
2041
2750
  });
2042
2751
  }
2043
2752
  return out;
@@ -2047,13 +2756,15 @@
2047
2756
  const px = i === N - 1 ? comps.max : comps.min + step * i;
2048
2757
  const yh = predict(px);
2049
2758
  const errs = stdErrorsAt(px, comps);
2759
+ const meanHalfWidth = confidenceHalfWidth(z, errs.seMean);
2760
+ const predHalfWidth = confidenceHalfWidth(z, errs.sePred);
2050
2761
  out.push({
2051
2762
  x: px,
2052
2763
  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
2764
+ lower: yh - meanHalfWidth,
2765
+ upper: yh + meanHalfWidth,
2766
+ predLower: yh - predHalfWidth,
2767
+ predUpper: yh + predHalfWidth
2057
2768
  });
2058
2769
  }
2059
2770
  return out;
@@ -2066,193 +2777,226 @@
2066
2777
  };
2067
2778
  }
2068
2779
 
2069
- function tricube(u) {
2070
- const uu = Math.abs(u);
2071
- if (uu >= 1) {
2072
- return 0;
2073
- }
2074
- const t = 1 - uu * uu * uu;
2075
- return t * t * t;
2076
- }
2077
- function stratifiedSample(sortedData, maxSamples) {
2078
- const n = sortedData.length;
2079
- if (n <= maxSamples) {
2080
- return sortedData;
2081
- }
2082
- const sampled = [];
2083
- const step = n / maxSamples;
2084
- for (let i = 0; i < maxSamples; i++) {
2085
- const idx = Math.min(Math.floor(i * step), n - 1);
2086
- sampled.push(sortedData[idx]);
2087
- }
2088
- return sampled;
2089
- }
2780
+ /**
2781
+ * 局部线性 LOWESS。默认全量观测、span=2/3、三次稳健重加权。
2782
+ * delta 显式插值加速;maxSamples 仅保留显式选择的历史抽样用途。
2783
+ * predict 与 evaluateGrid 查询构建时固定的同一模型,不触发再拟合。
2784
+ */
2090
2785
  function regressionLowess(data, x = d => d.x, y = d => d.y, options = {}) {
2091
- var _a;
2092
- const span = options.span || 0.3;
2093
- const degree = options.degree === 0 ? 0 : 1;
2094
- const alpha = (_a = options.alpha) !== null && _a !== void 0 ? _a : 0.05;
2095
- const iterations = options.iterations == null ? 2 : options.iterations;
2096
- const maxSamples = options.maxSamples || 1000;
2097
- const rawPoints = [];
2098
- visitPoints(data, x, y, (dx, dy, index) => {
2099
- rawPoints.push({ x: dx, y: dy, index });
2100
- });
2101
- rawPoints.sort((a, b) => a.x - b.x);
2102
- const sampledPoints = stratifiedSample(rawPoints, maxSamples);
2103
- const n = sampledPoints.length;
2104
- const ptsX = new Array(n);
2105
- const ptsY = new Array(n);
2106
- for (let i = 0; i < n; i++) {
2107
- ptsX[i] = sampledPoints[i].x;
2108
- ptsY[i] = sampledPoints[i].y;
2109
- }
2110
- function predictSingle(x0, robustWeights) {
2111
- if (n === 0) {
2786
+ var _a, _b, _c, _d, _e;
2787
+ const span = (_a = options.span) !== null && _a !== void 0 ? _a : 2 / 3;
2788
+ const degree = (_b = options.degree) !== null && _b !== void 0 ? _b : 1;
2789
+ const iterations = (_c = options.iterations) !== null && _c !== void 0 ? _c : 3;
2790
+ const delta = (_d = options.delta) !== null && _d !== void 0 ? _d : 0;
2791
+ const alpha = (_e = options.alpha) !== null && _e !== void 0 ? _e : 0.05;
2792
+ if (!(span > 0 && span <= 1) ||
2793
+ !Number.isInteger(iterations) ||
2794
+ iterations < 0 ||
2795
+ !Number.isFinite(delta) ||
2796
+ delta < 0 ||
2797
+ (degree !== 0 && degree !== 1) ||
2798
+ (options.maxSamples != null && (!Number.isInteger(options.maxSamples) || options.maxSamples < 2))) {
2799
+ throw new RangeError('Invalid LOWESS span, degree, iterations, delta or maxSamples');
2800
+ }
2801
+ let pts = regressionPoints(data, x, y).sort((a, b) => a.x - b.x || a.y - b.y);
2802
+ if (options.maxSamples != null && pts.length > options.maxSamples) {
2803
+ const full = pts;
2804
+ const count = options.maxSamples;
2805
+ pts = Array.from({ length: count }, (_, i) => full[Math.floor((i * (full.length - 1)) / (count - 1))]);
2806
+ }
2807
+ const n = pts.length;
2808
+ const ptsX = pts.map(p => p.x);
2809
+ const ptsY = pts.map(p => p.y);
2810
+ const basis = regressionBasis(pts);
2811
+ const normalizedX = ptsX.map(v => normalizeRegression(v, basis.xCenter, basis.xScale));
2812
+ const normalizedY = ptsY.map(v => normalizeRegression(v, basis.yCenter, basis.yScale));
2813
+ const count = Math.min(n, Math.max(2, Math.floor(span * n + 1e-10)));
2814
+ let robustWeights;
2815
+ function exact(x0) {
2816
+ if (!n) {
2112
2817
  return 0;
2113
2818
  }
2819
+ const query = normalizeRegression(x0, basis.xCenter, basis.xScale);
2820
+ // 找到连续的最近邻窗口;相等距离选择较小的 x(ties 的 y 已排序)。
2114
2821
  let left = 0;
2115
- let right = n;
2822
+ let right = n - count;
2116
2823
  while (left < right) {
2117
- const mid = (left + right) >> 1;
2118
- if (ptsX[mid] < x0) {
2824
+ const mid = (left + right) >>> 1;
2825
+ const lowerDistance = x0 - ptsX[mid];
2826
+ const upperDistance = ptsX[mid + count] - x0;
2827
+ if (Number.isFinite(lowerDistance) && Number.isFinite(upperDistance)
2828
+ ? lowerDistance > upperDistance
2829
+ : query - normalizedX[mid] > normalizedX[mid + count] - query) {
2119
2830
  left = mid + 1;
2120
2831
  }
2121
2832
  else {
2122
2833
  right = mid;
2123
2834
  }
2124
2835
  }
2125
- const m = Math.min(n, Math.max(2, Math.floor(span * n)));
2126
- let lo = left - 1;
2127
- let hi = left;
2128
- for (let count = 0; count < m; count++) {
2129
- if (lo >= 0 && (hi >= n || x0 - ptsX[lo] <= ptsX[hi] - x0)) {
2130
- lo--;
2836
+ let start = left;
2837
+ let end = left + count;
2838
+ if (ptsX[start] === ptsX[end - 1]) {
2839
+ while (start > 0 && ptsX[start - 1] === ptsX[start]) {
2840
+ start--;
2131
2841
  }
2132
- else {
2133
- hi++;
2134
- }
2135
- }
2136
- const actualStart = lo + 1;
2137
- const end = hi;
2138
- let maxDist = 0;
2139
- const windowSize = end - actualStart;
2140
- const distances = new Array(windowSize);
2141
- for (let i = actualStart; i < end; i++) {
2142
- const dist = Math.abs(ptsX[i] - x0);
2143
- distances[i - actualStart] = dist;
2144
- if (dist > maxDist) {
2145
- maxDist = dist;
2146
- }
2147
- }
2148
- let sumw = 0;
2149
- const w = new Array(windowSize);
2150
- for (let i = 0; i < windowSize; i++) {
2151
- const u = maxDist === 0 ? 0 : distances[i] / maxDist;
2152
- let wi = tricube(u);
2153
- if (robustWeights && robustWeights[actualStart + i] != null) {
2154
- wi *= robustWeights[actualStart + i];
2155
- }
2156
- w[i] = wi;
2157
- sumw += wi;
2158
- }
2159
- if (sumw === 0) {
2160
- let nearestIdx = 0;
2161
- let nearestDistance = Math.abs(ptsX[0] - x0);
2162
- for (let i = 1; i < n; i++) {
2163
- const distance = Math.abs(ptsX[i] - x0);
2164
- if (distance < nearestDistance ||
2165
- (distance === nearestDistance && sampledPoints[i].index < sampledPoints[nearestIdx].index)) {
2166
- nearestIdx = i;
2167
- nearestDistance = distance;
2168
- }
2842
+ while (end < n && ptsX[end] === ptsX[start]) {
2843
+ end++;
2169
2844
  }
2170
- return ptsY[nearestIdx];
2171
2845
  }
2172
- if (degree === 0) {
2173
- let s = 0;
2174
- for (let i = 0; i < w.length; i++) {
2175
- s += w[i] * ptsY[actualStart + i];
2176
- }
2177
- return s / sumw;
2846
+ const rawRadius = Math.max(Math.abs(ptsX[start] - x0), Math.abs(ptsX[end - 1] - x0));
2847
+ const scaled = !Number.isFinite(rawRadius);
2848
+ const radius = scaled
2849
+ ? Math.max(Math.abs(normalizedX[start] - query), Math.abs(normalizedX[end - 1] - query))
2850
+ : rawRadius;
2851
+ // 局部响应坐标不能由远处、未参与该邻域的观测决定精度。
2852
+ let yMin = Infinity;
2853
+ let yMax = -Infinity;
2854
+ for (let i = start; i < end; i++) {
2855
+ yMin = Math.min(yMin, ptsY[i]);
2856
+ yMax = Math.max(yMax, ptsY[i]);
2178
2857
  }
2858
+ const yCenter = yMin / 2 + yMax / 2;
2859
+ const yScale = Math.max(Math.abs(yMin - yCenter), Math.abs(yMax - yCenter)) || 1;
2179
2860
  let sw = 0;
2180
2861
  let sx = 0;
2181
2862
  let sy = 0;
2182
2863
  let sxx = 0;
2183
2864
  let sxy = 0;
2184
- for (let i = actualStart; i < end; i++) {
2185
- const idx = i - actualStart;
2186
- const xi = ptsX[i];
2187
- const yi = ptsY[i];
2188
- const wi = w[idx];
2189
- sw += wi;
2190
- sx += wi * xi;
2191
- sy += wi * yi;
2192
- sxx += wi * xi * xi;
2193
- sxy += wi * xi * yi;
2194
- }
2195
- const meanX = sx / sw;
2196
- const meanY = sy / sw;
2197
- const denom = sxx - sx * meanX;
2198
- const slope = Math.abs(denom) < 1e-12 ? 0 : (sxy - sx * meanY) / denom;
2199
- const intercept = meanY - slope * meanX;
2200
- return intercept + slope * x0;
2865
+ for (let i = start; i < end; i++) {
2866
+ const z = radius === 0 ? 0 : (scaled ? normalizedX[i] - query : ptsX[i] - x0) / radius;
2867
+ const u = Math.abs(z);
2868
+ const t = u >= 1 ? 0 : 1 - u * u * u;
2869
+ const w = t * t * t * (robustWeights ? robustWeights[i] : 1);
2870
+ sw += w;
2871
+ sx += w * z;
2872
+ const response = normalizeRegression(ptsY[i], yCenter, yScale);
2873
+ sy += w * response;
2874
+ sxx += w * z * z;
2875
+ sxy += w * z * response;
2876
+ }
2877
+ if (sw === 0) {
2878
+ // 核边界零权重时使用最近有效观测的加权常数;等距离取均值。
2879
+ let distance = Infinity;
2880
+ let weight = 0;
2881
+ let value = 0;
2882
+ for (let i = 0; i < n; i++) {
2883
+ const w = robustWeights ? robustWeights[i] : 1;
2884
+ if (w === 0) {
2885
+ continue;
2886
+ }
2887
+ const d = Math.abs(scaled ? normalizedX[i] - query : ptsX[i] - x0);
2888
+ if (d < distance) {
2889
+ distance = d;
2890
+ weight = w;
2891
+ value = ptsY[i];
2892
+ }
2893
+ else if (d === distance) {
2894
+ value = value * (weight / (weight + w)) + ptsY[i] * (w / (weight + w));
2895
+ weight += w;
2896
+ }
2897
+ }
2898
+ if (weight === 0) {
2899
+ throw new RangeError('LOWESS has no effective local observations');
2900
+ }
2901
+ return value;
2902
+ }
2903
+ const mx = sx / sw;
2904
+ const my = sy / sw;
2905
+ const variance = sxx / sw - mx * mx;
2906
+ const slope = degree === 0 || variance <= Number.EPSILON * 32 ? 0 : (sxy / sw - mx * my) / variance;
2907
+ return yCenter + yScale * (my - slope * mx);
2908
+ }
2909
+ // delta 不删除观测。只减少局部回归求值位置,全部残差参与重加权。
2910
+ const anchors = [];
2911
+ for (let i = 0; i < n;) {
2912
+ anchors.push(i);
2913
+ if (i === n - 1) {
2914
+ break;
2915
+ }
2916
+ let next = i + 1;
2917
+ if (delta > 0) {
2918
+ while (next + 1 < n && ptsX[next + 1] - ptsX[i] <= delta) {
2919
+ next++;
2920
+ }
2921
+ }
2922
+ // 重复 x 一起使用同一预测;端点不能丢失。
2923
+ while (next + 1 < n && ptsX[next + 1] === ptsX[next]) {
2924
+ next++;
2925
+ }
2926
+ i = next;
2201
2927
  }
2202
- function predict(x0) {
2203
- if (Array.isArray(x0)) {
2204
- const len = x0.length;
2205
- const out = new Array(len);
2206
- for (let i = 0; i < len; i++) {
2207
- out[i] = predictSingle(x0[i]);
2928
+ let fits = new Array(n);
2929
+ function fitObservations() {
2930
+ const out = new Array(n);
2931
+ for (let a = 0; a < anchors.length; a++) {
2932
+ const index = anchors[a];
2933
+ out[index] = exact(ptsX[index]);
2934
+ if (a === 0) {
2935
+ continue;
2936
+ }
2937
+ const previous = anchors[a - 1];
2938
+ const rawWidth = ptsX[index] - ptsX[previous];
2939
+ const width = Number.isFinite(rawWidth) ? rawWidth : normalizedX[index] - normalizedX[previous];
2940
+ for (let i = previous + 1; i < index; i++) {
2941
+ const t = width === 0
2942
+ ? 0
2943
+ : (Number.isFinite(rawWidth) ? ptsX[i] - ptsX[previous] : normalizedX[i] - normalizedX[previous]) / width;
2944
+ out[i] = out[previous] * (1 - t) + out[index] * t;
2208
2945
  }
2209
- return out;
2210
2946
  }
2211
- return predictSingle(x0);
2947
+ return out;
2212
2948
  }
2213
- function evaluateGrid(N) {
2214
- if (N <= 0) {
2215
- return [];
2949
+ for (let iteration = 0; iteration <= iterations; iteration++) {
2950
+ fits = fitObservations();
2951
+ if (iteration === iterations) {
2952
+ break;
2216
2953
  }
2217
- if (n === 0) {
2218
- return [];
2954
+ const residuals = fits.map((v, i) => {
2955
+ const residual = ptsY[i] - v;
2956
+ return Number.isFinite(residual)
2957
+ ? Math.abs(residual / basis.yScale)
2958
+ : Math.abs(normalizedY[i] - normalizeRegression(v, basis.yCenter, basis.yScale));
2959
+ });
2960
+ const med = median(residuals.slice());
2961
+ const currentFits = fits;
2962
+ robustWeights = residuals.map((r, i) => {
2963
+ if (med === 0) {
2964
+ const tolerance = Number.EPSILON * 64 * (Math.max(Math.abs(ptsY[i]), Math.abs(currentFits[i])) / basis.yScale);
2965
+ return r <= tolerance ? 1 : 0;
2966
+ }
2967
+ const u = r / (6 * med);
2968
+ return u >= 1 ? 0 : (1 - u * u) ** 2;
2969
+ });
2970
+ }
2971
+ function predictSingle(x0) {
2972
+ if (delta === 0 || n < 2 || x0 < ptsX[0] || x0 > ptsX[n - 1]) {
2973
+ return exact(x0);
2219
2974
  }
2220
- const out = new Array(N);
2221
- const min = ptsX[0];
2222
- const max = ptsX[n - 1];
2223
- if (min === max) {
2224
- const v = predictSingle(min);
2225
- for (let i = 0; i < N; i++) {
2226
- out[i] = { x: min, y: v };
2975
+ let lo = 0;
2976
+ let hi = n - 1;
2977
+ while (hi - lo > 1) {
2978
+ const mid = (lo + hi) >>> 1;
2979
+ if (ptsX[mid] <= x0) {
2980
+ lo = mid;
2227
2981
  }
2228
- return out;
2229
- }
2230
- const step = (max - min) / (N - 1);
2231
- let robustWeights;
2232
- const effectiveIterations = options.iterations != null ? iterations : n > 500 ? 0 : iterations;
2233
- if (effectiveIterations > 0) {
2234
- for (let iter = 0; iter < effectiveIterations; iter++) {
2235
- const fits = new Array(n);
2236
- const res = new Array(n);
2237
- for (let i = 0; i < n; i++) {
2238
- fits[i] = predictSingle(ptsX[i], robustWeights);
2239
- res[i] = Math.abs(ptsY[i] - fits[i]);
2240
- }
2241
- const sortedRes = res.slice().sort((a, b) => a - b);
2242
- const med = sortedRes[Math.floor(n / 2)] || 0;
2243
- robustWeights = new Array(n);
2244
- for (let i = 0; i < n; i++) {
2245
- const u = med === 0 ? 0 : res[i] / (6 * med);
2246
- const w = Math.abs(u) >= 1 ? 0 : (1 - u * u) * (1 - u * u);
2247
- robustWeights[i] = w;
2248
- }
2982
+ else {
2983
+ hi = mid;
2249
2984
  }
2250
2985
  }
2251
- for (let i = 0; i < N; i++) {
2252
- const px = i === N - 1 ? max : min + step * i;
2253
- out[i] = { x: px, y: predictSingle(px, robustWeights) };
2986
+ if (ptsX[hi] === ptsX[lo]) {
2987
+ return fits[lo];
2254
2988
  }
2255
- return out;
2989
+ const width = ptsX[hi] - ptsX[lo];
2990
+ const t = Number.isFinite(width)
2991
+ ? (x0 - ptsX[lo]) / width
2992
+ : (normalizeRegression(x0, basis.xCenter, basis.xScale) - normalizedX[lo]) / (normalizedX[hi] - normalizedX[lo]);
2993
+ return fits[lo] * (1 - t) + fits[hi] * t;
2994
+ }
2995
+ function predict(x0) {
2996
+ return Array.isArray(x0) ? x0.map(predictSingle) : predictSingle(x0);
2997
+ }
2998
+ function evaluateGrid(N) {
2999
+ return regressionGrid(ptsX[0], ptsX[n - 1], N, predictSingle);
2256
3000
  }
2257
3001
  function confidenceInterval(N = 50) {
2258
3002
  const out = [];
@@ -2262,6 +3006,7 @@
2262
3006
  if (n === 0) {
2263
3007
  return out;
2264
3008
  }
3009
+ // use data x-range (already sorted)
2265
3010
  const min = ptsX[0];
2266
3011
  const max = ptsX[n - 1];
2267
3012
  if (min === undefined || max === undefined || min === Infinity || max === -Infinity) {
@@ -2271,18 +3016,20 @@
2271
3016
  if (comps.n === 0) {
2272
3017
  return out;
2273
3018
  }
2274
- const z = Math.abs(invNorm(1 - alpha / 2));
3019
+ const z = confidenceCriticalValue(alpha);
2275
3020
  if (comps.min === comps.max) {
2276
3021
  const v = predictSingle(comps.min);
2277
3022
  const errs = stdErrorsAt(comps.min, comps);
3023
+ const meanHalfWidth = confidenceHalfWidth(z, errs.seMean);
3024
+ const predHalfWidth = confidenceHalfWidth(z, errs.sePred);
2278
3025
  for (let i = 0; i < N; i++) {
2279
3026
  out.push({
2280
3027
  x: comps.min,
2281
3028
  mean: v,
2282
- lower: v - z * errs.seMean,
2283
- upper: v + z * errs.seMean,
2284
- predLower: v - z * errs.sePred,
2285
- predUpper: v + z * errs.sePred
3029
+ lower: v - meanHalfWidth,
3030
+ upper: v + meanHalfWidth,
3031
+ predLower: v - predHalfWidth,
3032
+ predUpper: v + predHalfWidth
2286
3033
  });
2287
3034
  }
2288
3035
  return out;
@@ -2292,13 +3039,15 @@
2292
3039
  const px = i === N - 1 ? max : min + step * i;
2293
3040
  const yh = predictSingle(px);
2294
3041
  const errs = stdErrorsAt(px, comps);
3042
+ const meanHalfWidth = confidenceHalfWidth(z, errs.seMean);
3043
+ const predHalfWidth = confidenceHalfWidth(z, errs.sePred);
2295
3044
  out.push({
2296
3045
  x: px,
2297
3046
  mean: yh,
2298
- lower: yh - z * errs.seMean,
2299
- upper: yh + z * errs.seMean,
2300
- predLower: yh - z * errs.sePred,
2301
- predUpper: yh + z * errs.sePred
3047
+ lower: yh - meanHalfWidth,
3048
+ upper: yh + meanHalfWidth,
3049
+ predLower: yh - predHalfWidth,
3050
+ predUpper: yh + predHalfWidth
2302
3051
  });
2303
3052
  }
2304
3053
  return out;
@@ -2307,106 +3056,30 @@
2307
3056
  predict,
2308
3057
  evaluate: predict,
2309
3058
  evaluateGrid,
2310
- confidenceInterval
3059
+ confidenceInterval,
3060
+ // 节点值按 x 排序,重复观测仍保持其统计权重。
3061
+ fitted: pts.map((p, i) => ({ x: p.x, y: fits[i] }))
2311
3062
  };
2312
3063
  }
2313
3064
 
2314
- function solveLinearSystem(A, b) {
2315
- const n = b.length;
2316
- const M = new Array(n);
2317
- for (let i = 0; i < n; i++) {
2318
- M[i] = A[i].slice();
2319
- M[i].push(b[i]);
2320
- }
2321
- for (let k = 0; k < n; k++) {
2322
- let maxRow = k;
2323
- let maxVal = Math.abs(M[k][k]);
2324
- for (let i = k + 1; i < n; i++) {
2325
- const v = Math.abs(M[i][k]);
2326
- if (v > maxVal) {
2327
- maxVal = v;
2328
- maxRow = i;
2329
- }
2330
- }
2331
- if (maxRow !== k) {
2332
- const tmp = M[k];
2333
- M[k] = M[maxRow];
2334
- M[maxRow] = tmp;
2335
- }
2336
- if (Math.abs(M[k][k]) < 1e-12) {
2337
- const res = new Array(n).fill(0);
2338
- return res;
2339
- }
2340
- for (let j = k + 1; j <= n; j++) {
2341
- M[k][j] = M[k][j] / M[k][k];
2342
- }
2343
- M[k][k] = 1;
2344
- for (let i = 0; i < n; i++) {
2345
- if (i === k) {
2346
- continue;
2347
- }
2348
- const factor = M[i][k];
2349
- if (factor === 0) {
2350
- continue;
2351
- }
2352
- for (let j = k + 1; j <= n; j++) {
2353
- M[i][j] -= factor * M[k][j];
2354
- }
2355
- M[i][k] = 0;
2356
- }
2357
- }
2358
- const x = new Array(n);
2359
- for (let i = 0; i < n; i++) {
2360
- x[i] = M[i][n];
2361
- }
2362
- return x;
2363
- }
2364
3065
  function regressionPolynomial(data, x = d => d.x, y = d => d.y, options = {}) {
2365
3066
  var _a, _b;
2366
- let degree = (_a = options.degree) !== null && _a !== void 0 ? _a : 0;
2367
- if (degree < 0) {
2368
- degree = 0;
3067
+ const degree = (_a = options.degree) !== null && _a !== void 0 ? _a : 0;
3068
+ if (!Number.isInteger(degree) || degree < 0) {
3069
+ throw new RangeError('Polynomial degree must be an nonnegative integer');
2369
3070
  }
2370
3071
  const alpha = (_b = options.alpha) !== null && _b !== void 0 ? _b : 0.05;
2371
- const m = degree + 1;
2372
- const sums = new Array(2 * degree + 1).fill(0);
2373
- visitPoints(data, x, y, (dx, dy) => {
2374
- let xp = 1;
2375
- for (let k = 0; k < sums.length; k++) {
2376
- sums[k] += xp;
2377
- xp *= dx;
2378
- }
2379
- });
2380
- const A = new Array(m);
2381
- for (let i = 0; i < m; i++) {
2382
- A[i] = new Array(m).fill(0);
2383
- for (let j = 0; j < m; j++) {
2384
- A[i][j] = sums[i + j];
2385
- }
2386
- }
2387
- const B = new Array(m).fill(0);
2388
- visitPoints(data, x, y, (dx, dy) => {
2389
- let xp = 1;
2390
- for (let k = 0; k < m; k++) {
2391
- B[k] += dy * xp;
2392
- xp *= dx;
2393
- }
2394
- });
2395
- const coef = solveLinearSystem(A, B);
2396
- const predict = (xx) => {
2397
- let xp = 1;
2398
- let v = 0;
2399
- for (let k = 0; k < coef.length; k++) {
2400
- v += coef[k] * xp;
2401
- xp *= xx;
2402
- }
2403
- return v;
2404
- };
3072
+ const points = regressionPoints(data, x, y);
3073
+ const model = fitRegression(points, degree);
3074
+ const coef = regressionCoefficients(model);
3075
+ const predict = (xx) => regressionPrediction(model, xx);
2405
3076
  return {
2406
3077
  degree,
3078
+ rank: model.rank,
2407
3079
  coef,
2408
3080
  predict,
2409
3081
  rSquared: rSquared(data, x, y, (() => {
3082
+ // compute mean y
2410
3083
  let sum = 0;
2411
3084
  let cnt = 0;
2412
3085
  visitPoints(data, x, y, (_dx, dy) => {
@@ -2416,38 +3089,13 @@
2416
3089
  return cnt === 0 ? 0 : sum / cnt;
2417
3090
  })(), predict),
2418
3091
  evaluateGrid(N) {
2419
- const out = [];
2420
- if (N <= 0) {
2421
- return out;
2422
- }
2423
3092
  let min = Infinity;
2424
3093
  let max = -Infinity;
2425
- visitPoints(data, x, y, dx => {
2426
- if (!isNil(dx)) {
2427
- if (dx < min) {
2428
- min = dx;
2429
- }
2430
- if (dx > max) {
2431
- max = dx;
2432
- }
2433
- }
3094
+ points.forEach(p => {
3095
+ min = Math.min(min, p.x);
3096
+ max = Math.max(max, p.x);
2434
3097
  });
2435
- if (min === Infinity || max === -Infinity) {
2436
- return out;
2437
- }
2438
- if (min === max) {
2439
- const v = predict(min);
2440
- for (let i = 0; i < N; i++) {
2441
- out.push({ x: min, y: v });
2442
- }
2443
- return out;
2444
- }
2445
- const step = (max - min) / (N - 1);
2446
- for (let i = 0; i < N; i++) {
2447
- const px = i === N - 1 ? max : min + step * i;
2448
- out.push({ x: px, y: predict(px) });
2449
- }
2450
- return out;
3098
+ return regressionGrid(min, max, N, predict);
2451
3099
  },
2452
3100
  confidenceInterval(N = 50) {
2453
3101
  const out = [];
@@ -2458,18 +3106,20 @@
2458
3106
  if (comps.n === 0) {
2459
3107
  return out;
2460
3108
  }
2461
- const z = Math.abs(invNorm(1 - alpha / 2));
3109
+ const z = confidenceCriticalValue(alpha);
2462
3110
  if (comps.min === comps.max) {
2463
3111
  const v = predict(comps.min);
2464
3112
  const errs = stdErrorsAt(comps.min, comps);
3113
+ const meanHalfWidth = confidenceHalfWidth(z, errs.seMean);
3114
+ const predHalfWidth = confidenceHalfWidth(z, errs.sePred);
2465
3115
  for (let i = 0; i < N; i++) {
2466
3116
  out.push({
2467
3117
  x: comps.min,
2468
3118
  mean: v,
2469
- lower: v - z * errs.seMean,
2470
- upper: v + z * errs.seMean,
2471
- predLower: v - z * errs.sePred,
2472
- predUpper: v + z * errs.sePred
3119
+ lower: v - meanHalfWidth,
3120
+ upper: v + meanHalfWidth,
3121
+ predLower: v - predHalfWidth,
3122
+ predUpper: v + predHalfWidth
2473
3123
  });
2474
3124
  }
2475
3125
  return out;
@@ -2479,13 +3129,15 @@
2479
3129
  const px = i === N - 1 ? comps.max : comps.min + step * i;
2480
3130
  const yh = predict(px);
2481
3131
  const errs = stdErrorsAt(px, comps);
3132
+ const meanHalfWidth = confidenceHalfWidth(z, errs.seMean);
3133
+ const predHalfWidth = confidenceHalfWidth(z, errs.sePred);
2482
3134
  out.push({
2483
3135
  x: px,
2484
3136
  mean: yh,
2485
- lower: yh - z * errs.seMean,
2486
- upper: yh + z * errs.seMean,
2487
- predLower: yh - z * errs.sePred,
2488
- predUpper: yh + z * errs.sePred
3137
+ lower: yh - meanHalfWidth,
3138
+ upper: yh + meanHalfWidth,
3139
+ predLower: yh - predHalfWidth,
3140
+ predUpper: yh + predHalfWidth
2489
3141
  });
2490
3142
  }
2491
3143
  return out;
@@ -2493,14 +3145,29 @@
2493
3145
  };
2494
3146
  }
2495
3147
 
3148
+ /**
3149
+ * Kernel Density Estimation (KDE) utilities
3150
+ *
3151
+ * Exports:
3152
+ * - Kernel functions: gaussian, epanechnikov
3153
+ * - bandwidth selectors: scott, silverman
3154
+ * - kde: main function to compute density estimates at provided points
3155
+ */
3156
+ /** Gaussian kernel (standard normal) */
2496
3157
  const gaussian = (u) => {
2497
3158
  const invSqrt2Pi = 1 / Math.sqrt(2 * Math.PI);
2498
3159
  return invSqrt2Pi * Math.exp(-0.5 * u * u);
2499
3160
  };
3161
+ /** Epanechnikov kernel (compact support) */
2500
3162
  const epanechnikov = (u) => {
2501
3163
  const absu = Math.abs(u);
2502
3164
  return absu <= 1 ? 0.75 * (1 - u * u) : 0;
2503
3165
  };
3166
+ /**
3167
+ * Bandwidth selectors
3168
+ * - scott: h = n^{-1/(d+4)} * std
3169
+ * - silverman: h = ( (4/(d+2))^{1/(d+4)} ) * n^{-1/(d+4)} * std
3170
+ */
2504
3171
  function scott(n, std, dim = 1) {
2505
3172
  if (n <= 0 || std === 0) {
2506
3173
  return 0;
@@ -2514,6 +3181,9 @@
2514
3181
  const factor = Math.pow(4 / (dim + 2), 1 / (dim + 4));
2515
3182
  return factor * std * Math.pow(n, -1 / (dim + 4));
2516
3183
  }
3184
+ /**
3185
+ * Compute standard deviation for numeric array
3186
+ */
2517
3187
  function std(values) {
2518
3188
  const n = values.length;
2519
3189
  if (n === 0) {
@@ -2531,6 +3201,10 @@
2531
3201
  }
2532
3202
  return Math.sqrt(s / n);
2533
3203
  }
3204
+ /**
3205
+ * Factory: create a KDE evaluator for given data and options.
3206
+ * Usage: const model = kde(data, options); model.evaluate(x) or model.evaluate([x1,x2])
3207
+ */
2534
3208
  function kde(data, options = {}) {
2535
3209
  const n = data.length;
2536
3210
  const kernel = options.kernel || gaussian;
@@ -2545,6 +3219,7 @@
2545
3219
  h = scott(n, sd, 1);
2546
3220
  }
2547
3221
  }
3222
+ // if still zero (constant data), evaluator returns zeros
2548
3223
  if (!h || h <= 0) {
2549
3224
  const zerosEval = (x) => {
2550
3225
  if (Array.isArray(x)) {
@@ -2561,6 +3236,7 @@
2561
3236
  if (N <= 0) {
2562
3237
  return out;
2563
3238
  }
3239
+ // compute data min (use data range if available)
2564
3240
  let min = Infinity;
2565
3241
  let max = -Infinity;
2566
3242
  for (let j = 0; j < n; j++) {
@@ -2617,6 +3293,7 @@
2617
3293
  if (N <= 0) {
2618
3294
  return out;
2619
3295
  }
3296
+ // compute data range
2620
3297
  let min = Infinity;
2621
3298
  let max = -Infinity;
2622
3299
  for (let i = 0; i < n; i++) {
@@ -2632,6 +3309,7 @@
2632
3309
  return out;
2633
3310
  }
2634
3311
  if (min === max) {
3312
+ // single point repeated
2635
3313
  for (let i = 0; i < N; i++) {
2636
3314
  out.push({ x: min, y: evalPoint(min) });
2637
3315
  }
@@ -2647,6 +3325,14 @@
2647
3325
  };
2648
3326
  }
2649
3327
 
3328
+ /**
3329
+ * Empirical Cumulative Distribution Function (ECDF)
3330
+ * Returns a factory that builds an evaluator over provided samples.
3331
+ * The evaluator exposes:
3332
+ * - evaluate(x:number) => proportion of samples <= x
3333
+ * - evaluate(xs:number[]) => number[]
3334
+ * - evaluateGrid(N:number) => { points: number[], cdf: number[] } over sample range
3335
+ */
2650
3336
  function ecdf(data) {
2651
3337
  const n = data.length;
2652
3338
  const sorted = data.slice().sort((a, b) => a - b);
@@ -2654,8 +3340,9 @@
2654
3340
  if (n === 0) {
2655
3341
  return 0;
2656
3342
  }
3343
+ // count of values <= x (upper bound index)
2657
3344
  let lo = 0;
2658
- let hi = n;
3345
+ let hi = n; // exclusive
2659
3346
  while (lo < hi) {
2660
3347
  const mid = (lo + hi) >>> 1;
2661
3348
  if (sorted[mid] <= x) {
@@ -2707,8 +3394,241 @@
2707
3394
  };
2708
3395
  }
2709
3396
 
3397
+ function pearson(x, y) {
3398
+ let sx = 0;
3399
+ let sy = 0;
3400
+ for (let i = 0; i < x.length; i++) {
3401
+ sx = Math.max(sx, Math.abs(x[i]));
3402
+ sy = Math.max(sy, Math.abs(y[i]));
3403
+ }
3404
+ if (!sx || !sy) {
3405
+ return undefined;
3406
+ }
3407
+ let mx = 0;
3408
+ let my = 0;
3409
+ let cx = 0;
3410
+ let cy = 0;
3411
+ for (let i = 0; i < x.length; i++) {
3412
+ const vx = x[i] / sx - cx;
3413
+ const vy = y[i] / sy - cy;
3414
+ const tx = mx + vx;
3415
+ const ty = my + vy;
3416
+ cx = tx - mx - vx;
3417
+ cy = ty - my - vy;
3418
+ mx = tx;
3419
+ my = ty;
3420
+ }
3421
+ mx /= x.length;
3422
+ my /= x.length;
3423
+ let xx = 0;
3424
+ let yy = 0;
3425
+ let xy = 0;
3426
+ for (let i = 0; i < x.length; i++) {
3427
+ const dx = x[i] / sx - mx;
3428
+ const dy = y[i] / sy - my;
3429
+ xx += dx * dx;
3430
+ yy += dy * dy;
3431
+ xy += dx * dy;
3432
+ }
3433
+ if (xx === 0 || yy === 0) {
3434
+ return undefined;
3435
+ }
3436
+ const result = xy / Math.sqrt(xx * yy);
3437
+ // 仅修正浮点舍入越过理论端点的几个 ulp。
3438
+ if (Math.abs(result) > 1 && Math.abs(result) <= 1 + 8 * Number.EPSILON) {
3439
+ return Math.sign(result);
3440
+ }
3441
+ return result;
3442
+ }
3443
+ function ranks(values) {
3444
+ const order = values.map((value, index) => ({ value, index })).sort((a, b) => a.value - b.value);
3445
+ const result = new Array(values.length);
3446
+ for (let start = 0; start < order.length;) {
3447
+ let end = start + 1;
3448
+ while (end < order.length && order[end].value === order[start].value) {
3449
+ end++;
3450
+ }
3451
+ const rank = (start + end + 1) / 2;
3452
+ for (let i = start; i < end; i++) {
3453
+ result[order[i].index] = rank;
3454
+ }
3455
+ start = end;
3456
+ }
3457
+ return result;
3458
+ }
3459
+ function kendall(x, y) {
3460
+ const pairs = x.map((value, index) => ({ x: value, y: y[index] })).sort((a, b) => a.x - b.x || a.y - b.y);
3461
+ const sortedY = [...y].sort((a, b) => a - b);
3462
+ const yRanks = new Map();
3463
+ let tiesY = 0;
3464
+ for (let start = 0; start < sortedY.length;) {
3465
+ let end = start + 1;
3466
+ while (end < sortedY.length && sortedY[end] === sortedY[start]) {
3467
+ end++;
3468
+ }
3469
+ tiesY += ((end - start) * (end - start - 1)) / 2;
3470
+ yRanks.set(sortedY[start], yRanks.size + 1);
3471
+ start = end;
3472
+ }
3473
+ const tree = new Float64Array(yRanks.size + 1);
3474
+ let tiesX = 0;
3475
+ let tiesBoth = 0;
3476
+ let discordant = 0;
3477
+ for (let start = 0; start < pairs.length;) {
3478
+ let end = start + 1;
3479
+ while (end < pairs.length && pairs[end].x === pairs[start].x) {
3480
+ end++;
3481
+ }
3482
+ tiesX += ((end - start) * (end - start - 1)) / 2;
3483
+ for (let i = start; i < end;) {
3484
+ let j = i + 1;
3485
+ while (j < end && pairs[j].y === pairs[i].y) {
3486
+ j++;
3487
+ }
3488
+ tiesBoth += ((j - i) * (j - i - 1)) / 2;
3489
+ i = j;
3490
+ }
3491
+ // 同 x 组先查询再更新,避免将 x ties 算成逆序。
3492
+ for (let i = start; i < end; i++) {
3493
+ let lessOrEqual = 0;
3494
+ for (let rank = yRanks.get(pairs[i].y); rank > 0; rank -= rank & -rank) {
3495
+ lessOrEqual += tree[rank];
3496
+ }
3497
+ discordant += start - lessOrEqual;
3498
+ }
3499
+ for (let i = start; i < end; i++) {
3500
+ for (let rank = yRanks.get(pairs[i].y); rank < tree.length; rank += rank & -rank) {
3501
+ tree[rank]++;
3502
+ }
3503
+ }
3504
+ start = end;
3505
+ }
3506
+ const total = (x.length * (x.length - 1)) / 2;
3507
+ if (total === tiesX || total === tiesY) {
3508
+ return undefined;
3509
+ }
3510
+ return (total - tiesX - tiesY + tiesBoth - 2 * discordant) / Math.sqrt((total - tiesX) * (total - tiesY));
3511
+ }
3512
+ /** 等长有限样本的相关系数;常量/不足两条返回 undefined,不修改数组。 */
3513
+ function correlation(x, y, method = 'pearson') {
3514
+ if (x.length !== y.length ||
3515
+ !['pearson', 'spearman', 'kendall'].includes(method) ||
3516
+ x.some(value => !Number.isFinite(value)) ||
3517
+ y.some(value => !Number.isFinite(value))) {
3518
+ throw new RangeError('correlation requires equal finite arrays and a supported method');
3519
+ }
3520
+ if (x.length < 2) {
3521
+ return undefined;
3522
+ }
3523
+ return method === 'kendall' ? kendall(x, y) : method === 'spearman' ? pearson(ranks(x), ranks(y)) : pearson(x, y);
3524
+ }
3525
+
3526
+ /** 有限数值的均值;空输入没有均值,不修改输入。 */
3527
+ function mean(values) {
3528
+ if (!values.length) {
3529
+ return undefined;
3530
+ }
3531
+ let scale = 0;
3532
+ for (const value of values) {
3533
+ if (!Number.isFinite(value)) {
3534
+ throw new RangeError('mean requires finite numbers');
3535
+ }
3536
+ scale = Math.max(scale, Math.abs(value));
3537
+ }
3538
+ if (scale === 0) {
3539
+ return 0;
3540
+ }
3541
+ let sum = 0;
3542
+ let correction = 0;
3543
+ for (const value of values) {
3544
+ const next = value / scale;
3545
+ const total = sum + next;
3546
+ // Neumaier compensation retains small contributions even when larger values cancel.
3547
+ correction += Math.abs(sum) >= Math.abs(next) ? sum - total + next : next - total + sum;
3548
+ sum = total;
3549
+ }
3550
+ return ((sum + correction) / values.length) * scale;
3551
+ }
3552
+
3553
+ /** 有放回重采样;回调不得修改或持有复用的只读样本缓冲区。 */
3554
+ function bootstrapEstimate(values, estimator, options) {
3555
+ if (!values.length ||
3556
+ values.some(value => !Number.isFinite(value)) ||
3557
+ !Number.isInteger(options.resamples) ||
3558
+ options.resamples < 1 ||
3559
+ !Number.isInteger(options.seed) ||
3560
+ options.seed < 0 ||
3561
+ options.seed > 0xffffffff) {
3562
+ throw new RangeError('bootstrap requires finite observations, positive resamples and a uint32 seed');
3563
+ }
3564
+ const random = randomLCG(options.seed);
3565
+ const sample = new Array(values.length);
3566
+ const estimates = new Array(options.resamples);
3567
+ for (let b = 0; b < options.resamples; b++) {
3568
+ for (let i = 0; i < sample.length; i++) {
3569
+ sample[i] = values[Math.floor(random() * values.length)];
3570
+ }
3571
+ const estimate = estimator(sample);
3572
+ if (!Number.isFinite(estimate)) {
3573
+ throw new RangeError('bootstrap estimator must return a finite number');
3574
+ }
3575
+ estimates[b] = estimate;
3576
+ }
3577
+ return estimates;
3578
+ }
3579
+
3580
+ /** 成对 percentile bootstrap;每次重采样仅拟合一次,固定原始坐标基底。 */
3581
+ function regressionBootstrap(data, options) {
3582
+ var _a;
3583
+ const degree = options.method === 'linear' ? 1 : (_a = options.degree) !== null && _a !== void 0 ? _a : NaN;
3584
+ if ((options.method !== 'linear' && options.method !== 'polynomial') ||
3585
+ !Number.isInteger(degree) ||
3586
+ degree < 1 ||
3587
+ !data.length ||
3588
+ data.some(p => !Number.isFinite(p.x) || !Number.isFinite(p.y)) ||
3589
+ options.grid.some(x => !Number.isFinite(x)) ||
3590
+ !(options.level > 0 && options.level < 1) ||
3591
+ !Number.isInteger(options.resamples) ||
3592
+ options.resamples < 1 ||
3593
+ !Number.isInteger(options.seed) ||
3594
+ options.seed < 0 ||
3595
+ options.seed > 0xffffffff) {
3596
+ throw new RangeError('Invalid paired regression bootstrap inputs');
3597
+ }
3598
+ if (data.length <= degree + 1) {
3599
+ throw new RangeError('Regression bootstrap requires positive residual degrees of freedom');
3600
+ }
3601
+ const sorted = data.slice().sort((a, b) => a.x - b.x || a.y - b.y);
3602
+ const basis = regressionBasis(sorted);
3603
+ if (fitRegression(sorted, degree, basis).rank < degree + 1) {
3604
+ throw new RangeError('Original regression design is rank deficient');
3605
+ }
3606
+ const random = randomLCG(options.seed);
3607
+ const sample = new Array(sorted.length);
3608
+ const values = options.grid.map(() => new Array(options.resamples));
3609
+ for (let b = 0; b < options.resamples; b++) {
3610
+ for (let i = 0; i < sample.length; i++) {
3611
+ sample[i] = sorted[Math.floor(random() * sorted.length)];
3612
+ }
3613
+ const model = fitRegression(sample, degree, basis);
3614
+ for (let j = 0; j < options.grid.length; j++) {
3615
+ const value = regressionPrediction(model, options.grid[j]);
3616
+ if (!Number.isFinite(value)) {
3617
+ throw new RangeError('Regression bootstrap prediction is not finite');
3618
+ }
3619
+ values[j][b] = value;
3620
+ }
3621
+ }
3622
+ const tail = (1 - options.level) / 2;
3623
+ return options.grid.map((x, j) => {
3624
+ values[j].sort((a, b) => a - b);
3625
+ return { x, lower: quantileSorted(values[j], tail), upper: quantileSorted(values[j], 1 - tail) };
3626
+ });
3627
+ }
3628
+
2710
3629
  class HashValue {
2711
3630
  }
3631
+ // 基于 array 和 object的 HashTable
2712
3632
  class HashTable {
2713
3633
  constructor() {
2714
3634
  this.items = {};
@@ -2744,6 +3664,10 @@
2744
3664
  this.resetIndex();
2745
3665
  }
2746
3666
  }
3667
+ /**
3668
+ * 不包含当前index
3669
+ * @param index
3670
+ */
2747
3671
  delFrom(index) {
2748
3672
  for (let i = index + 1; i < this.count(); i++) {
2749
3673
  const key = this.itemList[i].key;
@@ -2787,6 +3711,7 @@
2787
3711
  getKeyByIndex(index) {
2788
3712
  return this.itemList[index].key;
2789
3713
  }
3714
+ //遍历 扩展
2790
3715
  foreach(callback) {
2791
3716
  for (const key in this.items) {
2792
3717
  const returnVal = callback(key, this.items[key].value);
@@ -2813,12 +3738,14 @@
2813
3738
  }
2814
3739
  return false;
2815
3740
  }
3741
+ //获取index
2816
3742
  indexOf(key) {
2817
3743
  if (this.has(key)) {
2818
3744
  return this.items[key].index;
2819
3745
  }
2820
3746
  return -1;
2821
3747
  }
3748
+ //插入
2822
3749
  insertAt(index, value, key) {
2823
3750
  const hashV = new HashValue();
2824
3751
  hashV.index = index;
@@ -2851,6 +3778,7 @@
2851
3778
  return returnArr;
2852
3779
  }
2853
3780
  toImmutableMap() {
3781
+ // do nothing
2854
3782
  }
2855
3783
  }
2856
3784
 
@@ -2877,6 +3805,15 @@
2877
3805
  function asin(x) {
2878
3806
  return x >= 1 ? halfPi : x <= -1 ? -halfPi : Math.asin(x);
2879
3807
  }
3808
+ /**
3809
+ * 根据比例获直线上的点
3810
+ * @param {number} x1 起始点 x
3811
+ * @param {number} y1 起始点 y
3812
+ * @param {number} x2 结束点 x
3813
+ * @param {number} y2 结束点 y
3814
+ * @param {number} t 指定比例
3815
+ * @return {object} 包含 x, y 的点
3816
+ */
2880
3817
  function pointAt(x1, y1, x2, y2, t) {
2881
3818
  let x = x2;
2882
3819
  let y = y2;
@@ -2892,6 +3829,7 @@
2892
3829
  };
2893
3830
  }
2894
3831
  function lengthFromPointToLine(point, point1, point2) {
3832
+ // 面积
2895
3833
  const dir1X = point2.x - point1.x;
2896
3834
  const dir1Y = point2.y - point1.y;
2897
3835
  const dir2X = point.x - point1.x;
@@ -2936,6 +3874,9 @@
2936
3874
 
2937
3875
  class Point {
2938
3876
  constructor(x = 0, y = 0, x1, y1) {
3877
+ /**
3878
+ * 面积图的下点
3879
+ */
2939
3880
  this.x = 0;
2940
3881
  this.y = 0;
2941
3882
  this.x = x;
@@ -2988,6 +3929,7 @@
2988
3929
  }
2989
3930
  }
2990
3931
  class PointService {
3932
+ /* distance */
2991
3933
  static distancePP(p1, p2) {
2992
3934
  return sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2));
2993
3935
  }
@@ -2997,6 +3939,7 @@
2997
3939
  static distancePN(point, x, y) {
2998
3940
  return sqrt(pow(x - point.x, 2) + pow(y - point.y, 2));
2999
3941
  }
3942
+ /* point at */
3000
3943
  static pointAtPP(p1, p2, t) {
3001
3944
  return new Point((p2.x - p1.x) * t + p1.x, (p2.y - p1.y) * t + p1.y);
3002
3945
  }
@@ -3045,12 +3988,30 @@
3045
3988
  return new ImageData(flipClampBuffer, width, height);
3046
3989
  };
3047
3990
 
3991
+ /**
3992
+ * 处理角度相关的数据计算
3993
+ */
3994
+ /**
3995
+ * degree -> radian
3996
+ * @param degree
3997
+ * @returns radian
3998
+ */
3048
3999
  function degreeToRadian(degree) {
3049
4000
  return degree * (Math.PI / 180);
3050
4001
  }
4002
+ /**
4003
+ * radian -> degree
4004
+ * @param radian
4005
+ * @returns
4006
+ */
3051
4007
  function radianToDegree(radian) {
3052
4008
  return (radian * 180) / Math.PI;
3053
4009
  }
4010
+ /**
4011
+ * 对弧度进行格式化
4012
+ * @param a 传入的弧度
4013
+ * @returns
4014
+ */
3054
4015
  const clampRadian = (angle = 0) => {
3055
4016
  if (angle < 0) {
3056
4017
  while (angle < -tau) {
@@ -3064,14 +4025,28 @@
3064
4025
  }
3065
4026
  return angle;
3066
4027
  };
4028
+ // alias of clampRadian
3067
4029
  const clampAngleByRadian = clampRadian;
4030
+ /**
4031
+ * 对角度进行格式化
4032
+ * @param a 角度
4033
+ * @returns
4034
+ */
3068
4035
  const clampDegree = (a = 0) => {
3069
4036
  if (a > 360 || a < -360) {
3070
4037
  return a % 360;
3071
4038
  }
3072
4039
  return a;
3073
4040
  };
4041
+ // alias of clampDegree
3074
4042
  const clampAngleByDegree = clampDegree;
4043
+ /**
4044
+ * 根据弧度及半径计算极坐标系下的坐标点
4045
+ * @param centerX 圆心坐标
4046
+ * @param radius 半径
4047
+ * @param angleInRadian 弧度
4048
+ * @returns 返回笛卡尔坐标点
4049
+ */
3075
4050
  function polarToCartesian(center, radius, angleInRadian) {
3076
4051
  if (!radius) {
3077
4052
  return { x: center.x, y: center.y };
@@ -3081,6 +4056,14 @@
3081
4056
  y: center.y + radius * Math.sin(angleInRadian)
3082
4057
  };
3083
4058
  }
4059
+ /**
4060
+ * 根据笛卡尔坐标系坐标计算极坐标坐标点
4061
+ * @param point
4062
+ * @param center 极坐标系中心点坐标
4063
+ * @param startAngle 极坐标系起始角度
4064
+ * @param endAngle 极坐标系结束角度
4065
+ * @returns 极坐标系坐标点
4066
+ */
3084
4067
  function cartesianToPolar(point, center = { x: 0, y: 0 }, startAngle = 0, endAngle = 2 * Math.PI) {
3085
4068
  const { x, y } = point;
3086
4069
  const { x: centerX, y: centerY } = center;
@@ -3111,9 +4094,19 @@
3111
4094
  angle: radian
3112
4095
  };
3113
4096
  }
4097
+ /**
4098
+ * 根据点的笛卡尔坐标获取该点与圆心的连线同正 x 轴方向的夹角
4099
+ * @param center 圆心坐标
4100
+ * @param point 待求的点坐标
4101
+ * @returns 返回夹角对应的弧度值
4102
+ */
3114
4103
  function getAngleByPoint(center, point) {
3115
4104
  return Math.atan2(point.y - center.y, point.x - center.x);
3116
4105
  }
4106
+ /**
4107
+ * 角度标准化处理
4108
+ * @param angle 弧度角
4109
+ */
3117
4110
  function normalizeAngle(angle) {
3118
4111
  while (angle < 0) {
3119
4112
  angle += Math.PI * 2;
@@ -3123,6 +4116,14 @@
3123
4116
  }
3124
4117
  return angle;
3125
4118
  }
4119
+ /**
4120
+ * 指定开始角度和结束角度,计算这个范围内的边界角度,
4121
+ * 即起始角度以及经过的东、南、西、北四个方向的角度
4122
+ * 计算这个角度,可以用于计算一个弧度的bounding box 等
4123
+ * @param startAngle 起始角度的弧度值
4124
+ * @param endAngle 结束角度的弧度值
4125
+ * @returns 边界角度数组
4126
+ */
3126
4127
  function findBoundaryAngles(startAngle, endAngle) {
3127
4128
  const deltaAngle = Math.abs(endAngle - startAngle);
3128
4129
  if (deltaAngle >= 2 * Math.PI || 2 * Math.PI - deltaAngle < 1e-6) {
@@ -3141,6 +4142,14 @@
3141
4142
  }
3142
4143
  return steps;
3143
4144
  }
4145
+ /**
4146
+ * 计算指定范围内,指定中心的情况下,不超出边界的最大可用半径
4147
+ * @param rect 矩形的大小
4148
+ * @param center 中心点
4149
+ * @param startAngle 起始角度的弧度值
4150
+ * @param endAngle 结束角度的弧度值
4151
+ * @returns 最大半径
4152
+ */
3144
4153
  function calculateMaxRadius(rect, center, startAngle, endAngle) {
3145
4154
  const { x, y } = center;
3146
4155
  const steps = findBoundaryAngles(startAngle, endAngle);
@@ -3192,10 +4201,18 @@
3192
4201
  return 1;
3193
4202
  }
3194
4203
 
4204
+ /* 用于判断2d相交 */
3195
4205
  function sub(out, v1, v2) {
3196
4206
  out[0] = v1[0] - v2[0];
3197
4207
  out[1] = v1[1] - v2[1];
3198
4208
  }
4209
+ /**
4210
+ * 判断直线是否相交,投影法
4211
+ * @param left1
4212
+ * @param right1
4213
+ * @param left2
4214
+ * @param right2
4215
+ */
3199
4216
  function isIntersect(left1, right1, left2, right2) {
3200
4217
  let min1 = left1[0];
3201
4218
  let max1 = right1[0];
@@ -3222,6 +4239,15 @@
3222
4239
  }
3223
4240
  return true;
3224
4241
  }
4242
+ /**
4243
+ * 获取直线交点
4244
+ * 不相交返回false,共线返回true,相交返回交点
4245
+ * https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect/565282#565282
4246
+ * @param left1
4247
+ * @param right1
4248
+ * @param left2
4249
+ * @param right2
4250
+ */
3225
4251
  function getIntersectPoint(left1, right1, left2, right2) {
3226
4252
  if (!isIntersect(left1, right1, left2, right2)) {
3227
4253
  return false;
@@ -3231,9 +4257,16 @@
3231
4257
  const tempVec = [0, 0];
3232
4258
  sub(dir1, right1, left1);
3233
4259
  sub(dir2, right2, left2);
4260
+ // 判断共线
3234
4261
  if (fuzzyEqualVec(dir1, dir2)) {
3235
4262
  return true;
3236
4263
  }
4264
+ // https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect/565282#565282
4265
+ // line1: left1 + dir1 * t
4266
+ // line2: left2 + dir2 * u
4267
+ // 当 left1 + dir1 * t = left2 + dir2 * u => (left1 + dir1 * t) x dir2 = (left2 + dir2 * u) x dir2
4268
+ // => dir1 x dir2 * t = (left2 - left1) x dir2 => t = (left2 - left1) x dir2 / (dir1 x dir2)
4269
+ // 直线不平行,dir1 x dir2 ≠ 0,当0 <= t <= 1时,可以求出交点
3237
4270
  sub(tempVec, left2, left1);
3238
4271
  const t = crossProduct(tempVec, dir2) / crossProduct(dir1, dir2);
3239
4272
  if (t >= 0 && t <= 1) {
@@ -3241,6 +4274,13 @@
3241
4274
  }
3242
4275
  return false;
3243
4276
  }
4277
+ /**
4278
+ * 获取两个rect的相交部分
4279
+ * 如果有bbox为null,返回null,如果不相交返回{x1: 0, y1: 0, x2: 0, y2: 0}
4280
+ * @param bbox1
4281
+ * @param bbox2
4282
+ * @param format
4283
+ */
3244
4284
  function getRectIntersect(bbox1, bbox2, format) {
3245
4285
  if (bbox1 === null) {
3246
4286
  return bbox2;
@@ -3285,19 +4325,35 @@
3285
4325
  }
3286
4326
  return { x11, x12, y11, y12, x21, x22, y21, y22 };
3287
4327
  };
4328
+ /**
4329
+ * 矩形是否在另一个矩形内部
4330
+ * 返回InnerBBox
4331
+ * @param bbox1
4332
+ * @param bbox2
4333
+ * @param format
4334
+ */
3288
4335
  function rectInsideAnotherRect(bbox1, bbox2, format) {
3289
4336
  if (!bbox1 || !bbox2) {
3290
4337
  return exports.InnerBBox.NONE;
3291
4338
  }
3292
4339
  const { x11, x12, y11, y12, x21, x22, y21, y22 } = formatTwoBBox(bbox1, bbox2, format);
4340
+ // bbox1在bbox2内部
3293
4341
  if (x11 > x21 && x12 < x22 && y11 > y21 && y12 < y22) {
3294
4342
  return exports.InnerBBox.BBOX1;
3295
4343
  }
4344
+ // bbox2在bbox1内部
3296
4345
  if (x21 > x11 && x22 < x12 && y21 > y11 && y22 < y12) {
3297
4346
  return exports.InnerBBox.BBOX2;
3298
4347
  }
3299
4348
  return exports.InnerBBox.NONE;
3300
4349
  }
4350
+ /**
4351
+ * 两个矩形是否相交
4352
+ * 如果有矩形为null,判断为相交
4353
+ * @param bbox1
4354
+ * @param bbox2
4355
+ * @param format
4356
+ */
3301
4357
  function isRectIntersect(bbox1, bbox2, format) {
3302
4358
  if (bbox1 && bbox2) {
3303
4359
  if (!format) {
@@ -3314,6 +4370,12 @@
3314
4370
  }
3315
4371
  return true;
3316
4372
  }
4373
+ /**
4374
+ * 点在box内部
4375
+ * 如果bbox为null返回true
4376
+ * @param point
4377
+ * @param bbox
4378
+ */
3317
4379
  function pointInRect(point, bbox, format) {
3318
4380
  if (!bbox) {
3319
4381
  return true;
@@ -3333,9 +4395,20 @@
3333
4395
  }
3334
4396
  return point.x >= x11 && point.x <= x12 && point.y >= y11 && point.y <= y12;
3335
4397
  }
4398
+ // 参考https://github.com/francecil/leetcode/issues/1
4399
+ /**
4400
+ * 计算投影半径
4401
+ * @param {Array(Number)} checkAxis 检测轴 [cosθ,sinθ]
4402
+ * @param {Array} axis 目标轴 [x,y]
4403
+ */
3336
4404
  function getProjectionRadius(checkAxis, axis) {
3337
4405
  return Math.abs(axis[0] * checkAxis[0] + axis[1] * checkAxis[1]);
3338
4406
  }
4407
+ /**
4408
+ * 逆时针旋转矩阵(从正 x 轴开始,旋转到正 y 轴的方向)
4409
+ * [ cos(θ) -sin(θ) ]
4410
+ * [ sin(θ) cos(θ) ]
4411
+ */
3339
4412
  function rotatePoint({ x, y }, rad, origin = { x: 0, y: 0 }) {
3340
4413
  return {
3341
4414
  x: (x - origin.x) * Math.cos(rad) - (y - origin.y) * Math.sin(rad) + origin.x,
@@ -3348,6 +4421,10 @@
3348
4421
  y: (box.y1 + box.y2) / 2
3349
4422
  };
3350
4423
  }
4424
+ /**
4425
+ * 转化为顶点坐标数组
4426
+ * @param {Object} box
4427
+ */
3351
4428
  function toRect(box, isDeg) {
3352
4429
  const deg = isDeg ? degreeToRadian(box.angle) : box.angle;
3353
4430
  const cp = getCenterPoint(box);
@@ -3376,15 +4453,21 @@
3376
4453
  const vector = (start, end) => {
3377
4454
  return [end.x - start.x, end.y - start.y];
3378
4455
  };
4456
+ // 两个矩形的中心点
3379
4457
  const p1 = getCenterPoint(box1);
3380
4458
  const p2 = getCenterPoint(box2);
4459
+ // 向量 p1p2
3381
4460
  const vp1p2 = vector(p1, p2);
4461
+ //矩形1的两边向量
3382
4462
  const AB = vector(rect1[0], rect1[1]);
3383
4463
  const BC = vector(rect1[1], rect1[2]);
4464
+ //矩形2的两边向量
3384
4465
  const A1B1 = vector(rect2[0], rect2[1]);
3385
4466
  const B1C1 = vector(rect2[1], rect2[2]);
4467
+ // 矩形1 的两个弧度
3386
4468
  const deg11 = isDeg ? degreeToRadian(box1.angle) : box1.angle;
3387
4469
  let deg12 = isDeg ? degreeToRadian(90 - box1.angle) : box1.angle + halfPi;
4470
+ // 矩形2 的两个弧度
3388
4471
  const deg21 = isDeg ? degreeToRadian(box2.angle) : box2.angle;
3389
4472
  let deg22 = isDeg ? degreeToRadian(90 - box2.angle) : box2.angle + halfPi;
3390
4473
  if (deg12 > pi2) {
@@ -3393,10 +4476,12 @@
3393
4476
  if (deg22 > pi2) {
3394
4477
  deg22 -= pi2;
3395
4478
  }
4479
+ // 投影重叠
3396
4480
  const isCover = (checkAxisRadius, deg, targetAxis1, targetAxis2) => {
3397
4481
  const checkAxis = [Math.cos(deg), Math.sin(deg)];
3398
4482
  const targetAxisRadius = (getProjectionRadius(checkAxis, targetAxis1) + getProjectionRadius(checkAxis, targetAxis2)) / 2;
3399
4483
  const centerPointRadius = getProjectionRadius(checkAxis, vp1p2);
4484
+ // console.log('abc', `checkAxis:${checkAxis},三个投影:${checkAxisRadius}, ${targetAxisRadius}, ${centerPointRadius}`)
3400
4485
  return checkAxisRadius + targetAxisRadius > centerPointRadius;
3401
4486
  };
3402
4487
  return (isCover((box1.x2 - box1.x1) / 2, deg11, A1B1, B1C1) &&
@@ -3411,6 +4496,7 @@
3411
4496
  let y2;
3412
4497
  function getAABBFromPoints(points) {
3413
4498
  (x1 = Infinity), (y1 = Infinity), (x2 = -Infinity), (y2 = -Infinity);
4499
+ // todo 有优化空间
3414
4500
  points.forEach(point => {
3415
4501
  if (x1 > point.x) {
3416
4502
  x1 = point.x;
@@ -3462,6 +4548,7 @@
3462
4548
  y22 = temp;
3463
4549
  }
3464
4550
  }
4551
+ // 不相交直接跳过
3465
4552
  if (x11 >= x22 || x12 <= x21 || y11 >= y22 || y12 <= y21) {
3466
4553
  return [bounds1, bounds2];
3467
4554
  }
@@ -3483,6 +4570,7 @@
3483
4570
  const l = [];
3484
4571
  list.forEach(b => {
3485
4572
  let arr;
4573
+ // 相交
3486
4574
  if ((arr = unionAABB(baseBound, b)).length > 1) {
3487
4575
  l.push(b);
3488
4576
  return;
@@ -3596,8 +4684,10 @@
3596
4684
  };
3597
4685
  }
3598
4686
  estimate({ text, fontSize }) {
3599
- let eCharLen = 0;
3600
- let cCharLen = 0;
4687
+ // 假设只有英文和中文字符
4688
+ let eCharLen = 0; // 英文字符
4689
+ let cCharLen = 0; // 中文字符
4690
+ // 判断ascii码,如果是
3601
4691
  for (let i = 0; i < text.length; i++) {
3602
4692
  text.charCodeAt(i) < 128 ? eCharLen++ : cCharLen++;
3603
4693
  }
@@ -3615,6 +4705,14 @@
3615
4705
  }
3616
4706
 
3617
4707
  const EPSILON = 1e-8;
4708
+ // Adapted from https://github.com/apache/echarts/blob/master/src/util/graphic.ts by striezel
4709
+ // License: https://github.com/apache/echarts/blob/master/LICENSE
4710
+ /**
4711
+ * Return `true` if the given line (line `a`) and the given polygon
4712
+ * are intersect.
4713
+ * Note that we do not count colinear as intersect here because no
4714
+ * requirement for that. We could do that if required in future.
4715
+ */
3618
4716
  function lineIntersectPolygon(a1x, a1y, a2x, a2y, points) {
3619
4717
  for (let i = 0, p2 = points[points.length - 1]; i < points.length; i++) {
3620
4718
  const p = points[i];
@@ -3625,6 +4723,8 @@
3625
4723
  }
3626
4724
  return false;
3627
4725
  }
4726
+ // Adapted from https://github.com/ecomfe/zrender/blob/master/src/contain/polygon.ts by pissang
4727
+ // License: https://github.com/ecomfe/zrender/blob/master/LICENSE
3628
4728
  function polygonContainPoint(points, x, y) {
3629
4729
  let w = 0;
3630
4730
  let p = points[0];
@@ -3636,30 +4736,44 @@
3636
4736
  w += isPointInLine(p.x, p.y, p2.x, p2.y, x, y);
3637
4737
  p = p2;
3638
4738
  }
4739
+ // Close polygon
3639
4740
  const p0 = points[0];
3640
4741
  if (!isAroundEqual(p.x, p0.x) || !isAroundEqual(p.y, p0.y)) {
3641
4742
  w += isPointInLine(p.x, p.y, p0.x, p0.y, x, y);
3642
4743
  }
3643
4744
  return w !== 0;
3644
4745
  }
4746
+ // Adapted from https://github.com/ecomfe/zrender/blob/master/src/contain/windingLine.ts by pissang
4747
+ // License: https://github.com/ecomfe/zrender/blob/master/LICENSE
3645
4748
  function isPointInLine(x0, y0, x1, y1, x, y) {
3646
4749
  if ((y > y0 && y > y1) || (y < y0 && y < y1)) {
3647
4750
  return 0;
3648
4751
  }
4752
+ // Ignore horizontal line
3649
4753
  if (y1 === y0) {
3650
4754
  return 0;
3651
4755
  }
3652
4756
  const t = (y - y0) / (y1 - y0);
3653
4757
  let dir = y1 < y0 ? 1 : -1;
4758
+ // Avoid winding error when intersection point is the connect point of two line of polygon
3654
4759
  if (t === 1 || t === 0) {
3655
4760
  dir = y1 < y0 ? 0.5 : -0.5;
3656
4761
  }
3657
4762
  const x_ = t * (x1 - x0) + x0;
4763
+ // If (x, y) on the line, considered as "contain".
3658
4764
  return x_ === x ? Infinity : x_ > x ? dir : 0;
3659
4765
  }
4766
+ // Adapted from https://github.com/ecomfe/zrender/blob/master/src/contain/polygon.ts by pissang
4767
+ // License: https://github.com/ecomfe/zrender/blob/master/LICENSE
3660
4768
  function isAroundEqual(a, b) {
3661
4769
  return Math.abs(a - b) < EPSILON;
3662
4770
  }
4771
+ /**
4772
+ * polygon图元之间是否相交
4773
+ * @param pointsA 图元A的points
4774
+ * @param pointsB 图元B的points
4775
+ * @returns 是否相交
4776
+ */
3663
4777
  function polygonIntersectPolygon(pointsA, pointsB) {
3664
4778
  for (let i = 0; i < pointsB.length; i++) {
3665
4779
  if (polygonContainPoint(pointsA, pointsB[i].x, pointsB[i].y)) {
@@ -3716,9 +4830,11 @@
3716
4830
  let width = 0;
3717
4831
  for (const character of string) {
3718
4832
  const codePoint = character.codePointAt(0);
4833
+ // Ignore control characters
3719
4834
  if (codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f)) {
3720
4835
  continue;
3721
4836
  }
4837
+ // Ignore combining characters
3722
4838
  if (codePoint >= 0x300 && codePoint <= 0x36f) {
3723
4839
  continue;
3724
4840
  }
@@ -3745,6 +4861,7 @@
3745
4861
  };
3746
4862
  const ansiRegex = ({ onlyFirst = false } = {}) => {
3747
4863
  const pattern = [
4864
+ // eslint-disable-next-line max-len
3748
4865
  '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
3749
4866
  '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))'
3750
4867
  ].join('|');
@@ -4001,9 +5118,12 @@
4001
5118
  return 'N';
4002
5119
  };
4003
5120
  const emojiRegex = () => {
5121
+ // https://mths.be/emoji
5122
+ // eslint-disable-next-line max-len
4004
5123
  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;
4005
5124
  };
4006
5125
 
5126
+ // FIXME: from VRender
4007
5127
  function getContextFont(text, defaultAttr = {}, fontSizeScale) {
4008
5128
  if (!fontSizeScale) {
4009
5129
  fontSizeScale = 1;
@@ -4020,16 +5140,25 @@
4020
5140
 
4021
5141
  class TextMeasure {
4022
5142
  constructor(option, textSpec) {
5143
+ /** 数字大小缓存 */
4023
5144
  this._numberCharSize = null;
5145
+ /** 全角字符大小缓存 */
4024
5146
  this._fullCharSize = null;
5147
+ /** 字母大小缓存 */
4025
5148
  this._letterCharSize = null;
5149
+ /** 特殊字符缓存 */
4026
5150
  this._specialCharSizeMap = {};
5151
+ /** 内置测量 canvas */
4027
5152
  this._canvas = null;
4028
5153
  this._context = null;
4029
5154
  this._contextSaved = false;
5155
+ /** 是否不支持 canvas */
4030
5156
  this._notSupportCanvas = false;
5157
+ /** 是否不支持 vrender */
4031
5158
  this._notSupportVRender = false;
5159
+ /** 文字 spec */
4032
5160
  this._userSpec = {};
5161
+ /** 特殊字符集 */
4033
5162
  this.specialCharSet = '-/: .,@%\'"~';
4034
5163
  this._option = option;
4035
5164
  this._userSpec = textSpec !== null && textSpec !== void 0 ? textSpec : {};
@@ -4041,6 +5170,7 @@
4041
5170
  ? this.fullMeasure.bind(this)
4042
5171
  : this.measureWithNaiveCanvas.bind(this);
4043
5172
  }
5173
+ /** 初始化测量相关上下文 */
4044
5174
  initContext() {
4045
5175
  if (this._notSupportCanvas) {
4046
5176
  return false;
@@ -4054,6 +5184,7 @@
4054
5184
  typeof window.document !== 'undefined' &&
4055
5185
  globalThis &&
4056
5186
  isValid(globalThis.document)) {
5187
+ // 默认创建方法
4057
5188
  this._canvas = globalThis.document.createElement('canvas');
4058
5189
  }
4059
5190
  }
@@ -4094,6 +5225,7 @@
4094
5225
  lineHeight
4095
5226
  };
4096
5227
  }
5228
+ /** 计算文本宽高 */
4097
5229
  measure(text, method) {
4098
5230
  switch (method) {
4099
5231
  case 'vrender':
@@ -4108,12 +5240,13 @@
4108
5240
  return this.quickMeasure(text);
4109
5241
  }
4110
5242
  }
5243
+ /** 精确计算文本宽高 */
4111
5244
  fullMeasure(text) {
4112
5245
  if (isNil(text)) {
4113
5246
  return { width: 0, height: 0 };
4114
5247
  }
4115
5248
  if (isNil(this._option.getTextBounds) || !this._notSupportVRender) {
4116
- return this.measureWithNaiveCanvas(text);
5249
+ return this.measureWithNaiveCanvas(text); // 降级
4117
5250
  }
4118
5251
  const { fontFamily, fontSize, fontWeight, textAlign, textBaseline, ellipsis, limit, lineHeight } = this.textSpec;
4119
5252
  let size;
@@ -4133,17 +5266,18 @@
4133
5266
  }
4134
5267
  catch (e) {
4135
5268
  this._notSupportVRender = true;
4136
- size = this.measureWithNaiveCanvas(text);
5269
+ size = this.measureWithNaiveCanvas(text); // 降级
4137
5270
  }
4138
5271
  return size;
4139
5272
  }
5273
+ /** 通过原生 canvas 精确计算文本宽度,高度为估算 */
4140
5274
  measureWithNaiveCanvas(text) {
4141
5275
  return this._measureReduce(text, this._measureWithNaiveCanvas.bind(this));
4142
5276
  }
4143
5277
  _measureWithNaiveCanvas(text) {
4144
5278
  var _a;
4145
5279
  if (!this.initContext()) {
4146
- return this._quickMeasureWithoutCanvas(text);
5280
+ return this._quickMeasureWithoutCanvas(text); // 降级
4147
5281
  }
4148
5282
  const metrics = this._context.measureText(text);
4149
5283
  const { fontSize, lineHeight } = this.textSpec;
@@ -4154,6 +5288,7 @@
4154
5288
  fontBoundingBoxDescent: metrics.fontBoundingBoxDescent
4155
5289
  };
4156
5290
  }
5291
+ /** 快速估算文本宽高 */
4157
5292
  quickMeasure(text) {
4158
5293
  return this._measureReduce(text, this._quickMeasure.bind(this));
4159
5294
  }
@@ -4164,13 +5299,17 @@
4164
5299
  };
4165
5300
  for (let i = 0; i < text.length; i++) {
4166
5301
  const char = text[i];
5302
+ // 先判断是否特殊字符
4167
5303
  let size = this._measureSpecialChar(char);
5304
+ // 再判断是否数字
4168
5305
  if (isNil(size) && TextMeasure.NUMBERS_CHAR_SET.includes(char)) {
4169
5306
  size = this._measureNumberChar();
4170
5307
  }
5308
+ // 再判断全角字符
4171
5309
  if (isNil(size) && ['F', 'W'].includes(eastAsianCharacterInfo(char))) {
4172
5310
  size = this._measureFullSizeChar();
4173
5311
  }
5312
+ // 最后按半角字符处理
4174
5313
  if (isNil(size)) {
4175
5314
  size = this._measureLetterChar();
4176
5315
  }
@@ -4181,6 +5320,7 @@
4181
5320
  }
4182
5321
  return totalSize;
4183
5322
  }
5323
+ /** 不使用 canvas 来快速估算文本宽高 */
4184
5324
  quickMeasureWithoutCanvas(text) {
4185
5325
  return this._measureReduce(text, this._quickMeasureWithoutCanvas.bind(this));
4186
5326
  }
@@ -4193,6 +5333,7 @@
4193
5333
  const { fontSize, lineHeight } = this.textSpec;
4194
5334
  for (let i = 0; i < text.length; i++) {
4195
5335
  const char = text[i];
5336
+ // 判断全角字符
4196
5337
  const size = ['F', 'W'].includes(eastAsianCharacterInfo(char)) ? 1 : 0.53;
4197
5338
  totalSize.width += size * fontSize;
4198
5339
  }
@@ -4216,11 +5357,12 @@
4216
5357
  }
4217
5358
  return {
4218
5359
  width: textArr.reduce((maxWidth, cur) => Math.max(maxWidth, processor(cur).width), 0),
4219
- height: textArr.length * (((_a = lineHeight) !== null && _a !== void 0 ? _a : fontSize) + 1) + 1
5360
+ height: textArr.length * (((_a = lineHeight) !== null && _a !== void 0 ? _a : fontSize) + 1) + 1 // 经验值
4220
5361
  };
4221
5362
  }
4222
5363
  return processor(text.toString());
4223
5364
  }
5365
+ /** 测量一个数字 */
4224
5366
  _measureNumberChar() {
4225
5367
  if (isNil(this._numberCharSize)) {
4226
5368
  const numberBounds = this._standardMethod(TextMeasure.NUMBERS_CHAR_SET);
@@ -4233,12 +5375,14 @@
4233
5375
  }
4234
5376
  return this._numberCharSize;
4235
5377
  }
5378
+ /** 测量一个全角字符 */
4236
5379
  _measureFullSizeChar() {
4237
5380
  if (isNil(this._fullCharSize)) {
4238
5381
  this._fullCharSize = this._standardMethod(TextMeasure.FULL_SIZE_CHAR);
4239
5382
  }
4240
5383
  return this._fullCharSize;
4241
5384
  }
5385
+ /** 测量一个英文字符 */
4242
5386
  _measureLetterChar() {
4243
5387
  if (isNil(this._letterCharSize)) {
4244
5388
  const alphabetBounds = this._standardMethod(TextMeasure.ALPHABET_CHAR_SET);
@@ -4251,6 +5395,7 @@
4251
5395
  }
4252
5396
  return this._letterCharSize;
4253
5397
  }
5398
+ /** 测量一个特殊字符 */
4254
5399
  _measureSpecialChar(char) {
4255
5400
  if (isValid(this._specialCharSizeMap[char])) {
4256
5401
  return this._specialCharSizeMap[char];
@@ -4274,8 +5419,11 @@
4274
5419
  }
4275
5420
  }
4276
5421
  }
5422
+ /** 英文字母集 */
4277
5423
  TextMeasure.ALPHABET_CHAR_SET = 'abcdefghijklmnopqrstuvwxyz';
5424
+ /** 数字集 */
4278
5425
  TextMeasure.NUMBERS_CHAR_SET = '0123456789';
5426
+ /** 全角字符 */
4279
5427
  TextMeasure.FULL_SIZE_CHAR = '字';
4280
5428
 
4281
5429
  const calculateAnchorOfBounds = (bounds, anchorType) => {
@@ -4331,26 +5479,28 @@
4331
5479
  { x: Math.cos(a.angle), y: Math.sin(a.angle) },
4332
5480
  { x: -Math.sin(a.angle), y: Math.cos(a.angle) },
4333
5481
  { x: Math.cos(b.angle), y: Math.sin(a.angle) },
4334
- { x: -Math.sin(b.angle), y: Math.cos(a.angle) }
5482
+ { x: -Math.sin(b.angle), y: Math.cos(a.angle) } // Rect B's second axis
4335
5483
  ];
5484
+ // calculate the projection range of a rectangle on a given axis
4336
5485
  function getProjectionRange(obb, axisX, axisY) {
4337
5486
  const corners = obb.getRotatedCorners();
4338
5487
  const projections = corners.map(p => p.x * axisX + p.y * axisY);
4339
5488
  return { min: Math.min(...projections), max: Math.max(...projections) };
4340
5489
  }
5490
+ // Calculate distances for all axes
4341
5491
  let maxDistance = 0;
4342
5492
  for (const axis of axes) {
4343
5493
  const rangeA = getProjectionRange(a, axis.x, axis.y);
4344
5494
  const rangeB = getProjectionRange(b, axis.x, axis.y);
4345
5495
  let distance;
4346
5496
  if (rangeA.max < rangeB.min) {
4347
- distance = rangeB.min - rangeA.max;
5497
+ distance = rangeB.min - rangeA.max; // B is to the right of A
4348
5498
  }
4349
5499
  else if (rangeB.max < rangeA.min) {
4350
- distance = rangeA.min - rangeB.max;
5500
+ distance = rangeA.min - rangeB.max; // A is to the right of B
4351
5501
  }
4352
5502
  else {
4353
- distance = 0;
5503
+ distance = 0; // Overlapping
4354
5504
  }
4355
5505
  maxDistance = Math.max(maxDistance, distance);
4356
5506
  }
@@ -4359,6 +5509,7 @@
4359
5509
 
4360
5510
  function transformBoundsWithMatrix(out, bounds, matrix) {
4361
5511
  const { x1, y1, x2, y2 } = bounds;
5512
+ // 如果没有旋转和缩放,那就直接translate
4362
5513
  if (matrix.onlyTranslate()) {
4363
5514
  if (out !== bounds) {
4364
5515
  out.setValue(bounds.x1, bounds.y1, bounds.x2, bounds.y2);
@@ -4373,6 +5524,16 @@
4373
5524
  out.add(matrix.a * x1 + matrix.c * y2 + matrix.e, matrix.b * x1 + matrix.d * y2 + matrix.f);
4374
5525
  return bounds;
4375
5526
  }
5527
+ /**
5528
+ * 依据graphic的属性对bounds进行变换
5529
+ * @param bounds
5530
+ * @param x
5531
+ * @param y
5532
+ * @param scaleX
5533
+ * @param scaleY
5534
+ * @param angle
5535
+ * @param rotateCenter
5536
+ */
4376
5537
  function transformBounds(bounds, x, y, scaleX, scaleY, angle, rotateCenter) {
4377
5538
  if (abs(scaleX) <= epsilon || abs(scaleY) <= epsilon) {
4378
5539
  return;
@@ -4390,6 +5551,37 @@
4390
5551
  }
4391
5552
  bounds.translate(x, y);
4392
5553
  }
5554
+ /**
5555
+ Copyright (c) 2015-2021, University of Washington Interactive Data Lab
5556
+ All rights reserved.
5557
+
5558
+ Redistribution and use in source and binary forms, with or without
5559
+ modification, are permitted provided that the following conditions are met:
5560
+
5561
+ 1. Redistributions of source code must retain the above copyright notice, this
5562
+ list of conditions and the following disclaimer.
5563
+
5564
+ 2. Redistributions in binary form must reproduce the above copyright notice,
5565
+ this list of conditions and the following disclaimer in the documentation
5566
+ and/or other materials provided with the distribution.
5567
+
5568
+ 3. Neither the name of the copyright holder nor the names of its contributors
5569
+ may be used to endorse or promote products derived from this software
5570
+ without specific prior written permission.
5571
+
5572
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
5573
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5574
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
5575
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
5576
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5577
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
5578
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
5579
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
5580
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
5581
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5582
+ */
5583
+ // 基于Vega的Bounds重构
5584
+ // https://github.com/vega/vega/blob/825bfaba6ccfe3306183df22b8c955a07bb30714/packages/vega-scenegraph/src/Bounds.js
4393
5585
  class Bounds {
4394
5586
  constructor(bounds) {
4395
5587
  if (bounds) {
@@ -4496,6 +5688,11 @@
4496
5688
  const p = this.scalePoints(sx, sy, x, y);
4497
5689
  return this.clear().add(p[0], p[1]).add(p[2], p[3]);
4498
5690
  }
5691
+ /**
5692
+ * 并集
5693
+ * @param b
5694
+ * @returns
5695
+ */
4499
5696
  union(b) {
4500
5697
  if (b.x1 < this.x1) {
4501
5698
  this.x1 = b.x1;
@@ -4511,6 +5708,11 @@
4511
5708
  }
4512
5709
  return this;
4513
5710
  }
5711
+ /**
5712
+ * 交集
5713
+ * @param b
5714
+ * @returns
5715
+ */
4514
5716
  intersect(b) {
4515
5717
  if (b.x1 > this.x1) {
4516
5718
  this.x1 = b.x1;
@@ -4526,15 +5728,36 @@
4526
5728
  }
4527
5729
  return this;
4528
5730
  }
5731
+ /**
5732
+ * 是否包含b
5733
+ * @param b
5734
+ * @returns
5735
+ */
4529
5736
  encloses(b) {
4530
5737
  return b && this.x1 <= b.x1 && this.x2 >= b.x2 && this.y1 <= b.y1 && this.y2 >= b.y2;
4531
5738
  }
5739
+ /**
5740
+ * 是否共边
5741
+ * @param b
5742
+ * @returns
5743
+ */
4532
5744
  alignsWith(b) {
4533
5745
  return b && (this.x1 === b.x1 || this.x2 === b.x2 || this.y1 === b.y1 || this.y2 === b.y2);
4534
5746
  }
5747
+ /**
5748
+ * 是否相交
5749
+ * @param b
5750
+ * @returns
5751
+ */
4535
5752
  intersects(b) {
4536
5753
  return b && !(this.x2 < b.x1 || this.x1 > b.x2 || this.y2 < b.y1 || this.y1 > b.y2);
4537
5754
  }
5755
+ /**
5756
+ * 是否包含
5757
+ * @param x
5758
+ * @param y
5759
+ * @returns
5760
+ */
4538
5761
  contains(x = 0, y = 0) {
4539
5762
  return !(x < this.x1 || x > this.x2 || y < this.y1 || y > this.y2);
4540
5763
  }
@@ -4640,6 +5863,7 @@
4640
5863
  this.f = f;
4641
5864
  }
4642
5865
  equalToMatrix(m2) {
5866
+ // ef -> ad -> bc
4643
5867
  return !(this.e !== m2.e ||
4644
5868
  this.f !== m2.f ||
4645
5869
  this.a !== m2.a ||
@@ -4648,6 +5872,7 @@
4648
5872
  this.c !== m2.c);
4649
5873
  }
4650
5874
  equalTo(a, b, c, d, e, f) {
5875
+ // ef -> ad -> bc
4651
5876
  return !(this.e !== e || this.f !== f || this.a !== a || this.d !== d || this.b !== b || this.c !== c);
4652
5877
  }
4653
5878
  setValue(a, b, c, d, e, f) {
@@ -4668,6 +5893,9 @@
4668
5893
  this.f = 0;
4669
5894
  return this;
4670
5895
  }
5896
+ /**
5897
+ * 获取当前矩阵的逆矩阵
5898
+ */
4671
5899
  getInverse() {
4672
5900
  const a = this.a;
4673
5901
  const b = this.b;
@@ -4699,6 +5927,17 @@
4699
5927
  return this;
4700
5928
  }
4701
5929
  rotateByCenter(rad, cx, cy) {
5930
+ /** rotate matrix
5931
+ * | cos -sin (1-cos)*cx+sin*cy |
5932
+ * | sin cos (1-cos)*cy-sin*cx |
5933
+ * | 0 0 1 |
5934
+ * after multiply
5935
+ * m13 = (1-cos)*cx+sin*cy;
5936
+ * m23 = (1-cos)*cy-sin*cx;
5937
+ * | cos*a-sin*b cos*c-sin*d cos*e-sin*f+m13 |
5938
+ * | sin*a+cos*b sin*c+cos*d sin*e+cos*f+m23 |
5939
+ * | 0 0 1 |
5940
+ */
4702
5941
  const cos = Math.cos(rad);
4703
5942
  const sin = Math.sin(rad);
4704
5943
  const rotateM13 = (1 - cos) * cx + sin * cy;
@@ -4741,6 +5980,15 @@
4741
5980
  return this;
4742
5981
  }
4743
5982
  transpose() {
5983
+ /** transpose matrix
5984
+ * | 0 1 0 |
5985
+ * | 1 0 0 |
5986
+ * | 0 0 1 |
5987
+ * after multiply
5988
+ * | b d f |
5989
+ * | a c e |
5990
+ * | 0 0 1 |
5991
+ */
4744
5992
  const { a, b, c, d, e, f } = this;
4745
5993
  this.a = b;
4746
5994
  this.b = a;
@@ -4750,6 +5998,10 @@
4750
5998
  this.f = e;
4751
5999
  return this;
4752
6000
  }
6001
+ /**
6002
+ * 矩阵相乘
6003
+ * @param matrix
6004
+ */
4753
6005
  multiply(a2, b2, c2, d2, e2, f2) {
4754
6006
  const a1 = this.a;
4755
6007
  const b1 = this.b;
@@ -4771,6 +6023,11 @@
4771
6023
  this.f = dy;
4772
6024
  return this;
4773
6025
  }
6026
+ /**
6027
+ * 插值计算
6028
+ * @param m2
6029
+ * @param t
6030
+ */
4774
6031
  interpolate(m2, t) {
4775
6032
  const m = new Matrix();
4776
6033
  m.a = this.a + (m2.a - this.a) * t;
@@ -4781,6 +6038,11 @@
4781
6038
  m.f = this.f + (m2.f - this.f) * t;
4782
6039
  return m;
4783
6040
  }
6041
+ /**
6042
+ * 将point转到当前矩阵的坐标空间中
6043
+ * @param source
6044
+ * @param target
6045
+ */
4784
6046
  transformPoint(source, target) {
4785
6047
  const { a, b, c, d, e, f } = this;
4786
6048
  const dt = a * d - b * c;
@@ -4794,6 +6056,7 @@
4794
6056
  target.x = x * nextA + y * nextC + nextE;
4795
6057
  target.y = x * nextB + y * nextD + nextF;
4796
6058
  }
6059
+ // 只有translate
4797
6060
  onlyTranslate(scale = 1) {
4798
6061
  return this.a === scale && this.b === 0 && this.c === 0 && this.d === scale;
4799
6062
  }
@@ -4817,6 +6080,7 @@
4817
6080
  skewX: 0,
4818
6081
  skewY: 0
4819
6082
  };
6083
+ // Apply the QR-like decomposition.
4820
6084
  if (a !== 0 || b !== 0) {
4821
6085
  const r = Math.sqrt(a * a + b * b);
4822
6086
  result.rotateDeg = b > 0 ? Math.acos(a / r) : -Math.acos(a / r);
@@ -4838,6 +6102,17 @@
4838
6102
  return result;
4839
6103
  }
4840
6104
  }
6105
+ /**
6106
+ * 对matrix进行通常的变换(基于rotateCenter进行旋转的同时进行translate和scale)
6107
+ * @param out
6108
+ * @param origin
6109
+ * @param x
6110
+ * @param y
6111
+ * @param scaleX
6112
+ * @param scaleY
6113
+ * @param angle
6114
+ * @param rotateCenter
6115
+ */
4841
6116
  function normalTransform(out, origin, x, y, scaleX, scaleY, angle, rotateCenter) {
4842
6117
  const oa = origin.a;
4843
6118
  const ob = origin.b;
@@ -4871,18 +6146,20 @@
4871
6146
  out.f = of + ob * rotateCenterX + od * rotateCenterY - b1 * offsetX - d1 * offsetY;
4872
6147
  }
4873
6148
 
6149
+ // LRU清除
4874
6150
  class LRU {
4875
6151
  constructor() {
4876
6152
  this.CLEAN_THRESHOLD = 1e3;
4877
- this.L_TIME = 1000;
4878
- this.R_COUNT = 1;
4879
- this.R_TIMESTAMP_MAX_SIZE = 20;
6153
+ this.L_TIME = 1000; // 1000ms以内
6154
+ this.R_COUNT = 1; // 使用次数为0
6155
+ this.R_TIMESTAMP_MAX_SIZE = 20; // timestamp最大长度
4880
6156
  }
4881
6157
  clearCache(cache, params) {
4882
6158
  const { CLEAN_THRESHOLD = this.CLEAN_THRESHOLD, L_TIME = this.L_TIME, R_COUNT = this.R_COUNT } = params;
4883
6159
  if (cache.size < CLEAN_THRESHOLD) {
4884
6160
  return 0;
4885
6161
  }
6162
+ // 开始清理,清理最近1000ms内使用次数为0的缓存
4886
6163
  let clearNum = 0;
4887
6164
  const clear = (key) => {
4888
6165
  clearNum++;
@@ -4892,7 +6169,7 @@
4892
6169
  cache.forEach((item, key) => {
4893
6170
  if (item.timestamp.length < R_COUNT) {
4894
6171
  return clear(key);
4895
- }
6172
+ } // 最近使用次数小于R_COUNT,清除
4896
6173
  let useCount = 0;
4897
6174
  while (now - item.timestamp[item.timestamp.length - 1 - useCount] < L_TIME) {
4898
6175
  useCount++;
@@ -4902,7 +6179,8 @@
4902
6179
  }
4903
6180
  if (useCount < R_COUNT) {
4904
6181
  return clear(key);
4905
- }
6182
+ } // 最近L_TIME以内使用次数小于R_COUNT,清除
6183
+ // 清除L_TIME以外的key,避免数组过长
4906
6184
  while (now - item.timestamp[0] > L_TIME) {
4907
6185
  item.timestamp.shift();
4908
6186
  }
@@ -4913,6 +6191,7 @@
4913
6191
  addLimitedTimestamp(cacheItem, t, params) {
4914
6192
  const { R_TIMESTAMP_MAX_SIZE = this.R_TIMESTAMP_MAX_SIZE } = params;
4915
6193
  if (cacheItem.timestamp.length > R_TIMESTAMP_MAX_SIZE) {
6194
+ // cacheItem.timestamp[cacheItem.timestamp.length-1] =
4916
6195
  cacheItem.timestamp.shift();
4917
6196
  }
4918
6197
  cacheItem.timestamp.push(t);
@@ -4984,6 +6263,7 @@
4984
6263
  r /= 255;
4985
6264
  g /= 255;
4986
6265
  b /= 255;
6266
+ // Find greatest and smallest channel values
4987
6267
  const cMin = Math.min(r, g, b);
4988
6268
  const cMax = Math.max(r, g, b);
4989
6269
  const delta = cMax - cMin;
@@ -4993,12 +6273,15 @@
4993
6273
  if (delta === 0) {
4994
6274
  h = 0;
4995
6275
  }
6276
+ // Red is max
4996
6277
  else if (cMax === r) {
4997
6278
  h = ((g - b) / delta) % 6;
4998
6279
  }
6280
+ // Green is max
4999
6281
  else if (cMax === g) {
5000
6282
  h = (b - r) / delta + 2;
5001
6283
  }
6284
+ // Blue is max
5002
6285
  else {
5003
6286
  h = (r - g) / delta + 4;
5004
6287
  }
@@ -5007,7 +6290,9 @@
5007
6290
  h += 360;
5008
6291
  }
5009
6292
  l = (cMax + cMin) / 2;
6293
+ // Calculate saturation
5010
6294
  s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
6295
+ // Multiply l and s by 100
5011
6296
  s = +(s * 100).toFixed(1);
5012
6297
  l = +(l * 100).toFixed(1);
5013
6298
  return {
@@ -5017,6 +6302,34 @@
5017
6302
  };
5018
6303
  }
5019
6304
 
6305
+ /*
6306
+ Copyright (c) 2013, Jason Davies.
6307
+ All rights reserved.
6308
+
6309
+ Redistribution and use in source and binary forms, with or without
6310
+ modification, are permitted provided that the following conditions are met:
6311
+
6312
+ * Redistributions of source code must retain the above copyright notice, this
6313
+ list of conditions and the following disclaimer.
6314
+
6315
+ * Redistributions in binary form must reproduce the above copyright notice,
6316
+ this list of conditions and the following disclaimer in the documentation
6317
+ and/or other materials provided with the distribution.
6318
+
6319
+ * The name Jason Davies may not be used to endorse or promote products
6320
+ derived from this software without specific prior written permission.
6321
+
6322
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
6323
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
6324
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
6325
+ DISCLAIMED. IN NO EVENT SHALL JASON DAVIES BE LIABLE FOR ANY DIRECT, INDIRECT,
6326
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
6327
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
6328
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
6329
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
6330
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
6331
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
6332
+ */
5020
6333
  const REG_HEX = /^#([0-9a-f]{3,8})$/;
5021
6334
  const DEFAULT_COLORS_OPACITY = {
5022
6335
  transparent: 0xffffff00
@@ -5182,6 +6495,7 @@
5182
6495
  else if (isArray(value)) {
5183
6496
  return new RGB(value[0], value[1], value[2]);
5184
6497
  }
6498
+ // default return
5185
6499
  return new RGB(255, 255, 255);
5186
6500
  }
5187
6501
  function rgba(value) {
@@ -5191,6 +6505,7 @@
5191
6505
  else if (isArray(value)) {
5192
6506
  return new RGB(value[0], value[1], value[2], value[3]);
5193
6507
  }
6508
+ // default return
5194
6509
  return new RGB(255, 255, 255, 1);
5195
6510
  }
5196
6511
  function SRGBToLinear(c) {
@@ -5204,12 +6519,15 @@
5204
6519
  if (forceHex || isHex) {
5205
6520
  const hex = parseInt(isHex[1], 16);
5206
6521
  const hexLength = isHex[1].length;
6522
+ // #fff
5207
6523
  if (hexLength === 3) {
5208
6524
  return new RGB(((hex >> 8) & 0xf) + (((hex >> 8) & 0xf) << 4), ((hex >> 4) & 0xf) + (((hex >> 4) & 0xf) << 4), (hex & 0xf) + ((hex & 0xf) << 4), 1);
5209
6525
  }
6526
+ // #ffffff
5210
6527
  if (hexLength === 6) {
5211
6528
  return rgb(hex);
5212
6529
  }
6530
+ // #ffffffaa
5213
6531
  else if (hexLength === 8) {
5214
6532
  return new RGB((hex >> 24) & 0xff, (hex >> 16) & 0xff, (hex >> 8) & 0xff, (hex & 0xff) / 0xff);
5215
6533
  }
@@ -5250,21 +6568,26 @@
5250
6568
  }
5251
6569
  }
5252
6570
  static parseColorString(value) {
6571
+ // case0: transparent
5253
6572
  if (isValid(DEFAULT_COLORS_OPACITY[value])) {
5254
6573
  return rgba(DEFAULT_COLORS_OPACITY[value]);
5255
6574
  }
6575
+ // case1: 简写 ['blue','red']
5256
6576
  if (isValid(DEFAULT_COLORS[value])) {
5257
6577
  return rgb(DEFAULT_COLORS[value]);
5258
6578
  }
5259
6579
  const formatValue = `${value}`.trim().toLowerCase();
6580
+ // case2: 16进制
5260
6581
  const hexRes = setHex(formatValue);
5261
6582
  if (hexRes !== undefined) {
5262
6583
  return hexRes;
5263
6584
  }
6585
+ // case3 : rgb
5264
6586
  if (/^(rgb|RGB|rgba|RGBA)/.test(formatValue)) {
5265
6587
  const aColor = formatValue.replace(/(?:\(|\)|rgba|RGBA|rgb|RGB)*/g, '').split(',');
5266
6588
  return new RGB(parseInt(aColor[0], 10), parseInt(aColor[1], 10), parseInt(aColor[2], 10), parseFloat(aColor[3]));
5267
6589
  }
6590
+ // case4: hsl
5268
6591
  if (/^(hsl|HSL|hsla|HSLA)/.test(formatValue)) {
5269
6592
  const aColor = formatValue.replace(/(?:\(|\)|hsla|HSLA|hsl|HSL)*/g, '').split(',');
5270
6593
  const rgb = hslToRgb(parseInt(aColor[0], 10), parseInt(aColor[1], 10), parseInt(aColor[2], 10));
@@ -5274,10 +6597,12 @@
5274
6597
  }
5275
6598
  constructor(value) {
5276
6599
  const color = Color.parseColorString(value);
6600
+ // case0: transparent
5277
6601
  if (color) {
5278
6602
  this.color = color;
5279
6603
  }
5280
6604
  else {
6605
+ // default: 白色
5281
6606
  console.warn(`Warn: 传入${value}无法解析为Color`);
5282
6607
  this.color = new RGB(255, 255, 255);
5283
6608
  }
@@ -5321,6 +6646,7 @@
5321
6646
  this.color.b = Math.max(0, Math.min(255, Math.floor(b * color.color.b)));
5322
6647
  return this;
5323
6648
  }
6649
+ // todo: 这里亮度计算都没考虑alpha通道
5324
6650
  getHSVBrightness() {
5325
6651
  return Math.max(this.color.r, this.color.g, this.color.b) / 255;
5326
6652
  }
@@ -5348,11 +6674,14 @@
5348
6674
  return res !== null && res !== void 0 ? res : this;
5349
6675
  }
5350
6676
  setColorName(name) {
6677
+ // color keywords
5351
6678
  const hex = DEFAULT_COLORS[name.toLowerCase()];
5352
6679
  if (typeof hex !== 'undefined') {
6680
+ // red
5353
6681
  this.setHex(hex);
5354
6682
  }
5355
6683
  else {
6684
+ // unknown color
5356
6685
  console.warn('THREE.Color: Unknown color ' + name);
5357
6686
  }
5358
6687
  return this;
@@ -5376,6 +6705,9 @@
5376
6705
  getLuminance3() {
5377
6706
  return (0.299 * this.color.r + 0.587 * this.color.g + 0.114 * this.color.b) / 255;
5378
6707
  }
6708
+ /**
6709
+ * https://www.w3.org/TR/WCAG21/#use-of-color
6710
+ */
5379
6711
  getLuminanceWCAG() {
5380
6712
  const RsRGB = this.color.r / 255;
5381
6713
  const GsRGB = this.color.g / 255;
@@ -5461,6 +6793,7 @@
5461
6793
  this.opacity = 1;
5462
6794
  }
5463
6795
  }
6796
+ // 转为16进制
5464
6797
  formatHex() {
5465
6798
  return `#${hex(this.r) + hex(this.g) + hex(this.b) + (this.opacity === 1 ? '' : hex(this.opacity * 255))}`;
5466
6799
  }
@@ -5473,11 +6806,17 @@
5473
6806
  const { h, s, l } = rgbToHsl(this.r, this.g, this.b);
5474
6807
  return `${opacity === 1 ? 'hsl(' : 'hsla('}${h},${s}%,${l}%${opacity === 1 ? ')' : `,${opacity})`}`;
5475
6808
  }
6809
+ // 转为16进制
5476
6810
  toString() {
5477
6811
  return this.formatHex();
5478
6812
  }
5479
6813
  }
5480
6814
 
6815
+ /**
6816
+ * 将 hex 格式颜色转换为 rgb 格式
6817
+ * @param str hex 格式的颜色值
6818
+ * @returns rgb 格式
6819
+ */
5481
6820
  function hexToRgb(str) {
5482
6821
  let r = '';
5483
6822
  let g = '';
@@ -5539,6 +6878,11 @@
5539
6878
  rgbToHsl: rgbToHsl
5540
6879
  });
5541
6880
 
6881
+ /**
6882
+ * 将 padding 转换为通用的格式([上,右,下,左])
6883
+ * @param padding
6884
+ * @return [ top, right, bottom, left ]
6885
+ */
5542
6886
  function normalizePadding(padding) {
5543
6887
  if (isValidNumber(padding)) {
5544
6888
  return [padding, padding, padding, padding];
@@ -5610,6 +6954,12 @@
5610
6954
  function millisecondsSetterName(isUTC) {
5611
6955
  return isUTC ? 'setUTCMilliseconds' : 'setMilliseconds';
5612
6956
  }
6957
+ /**
6958
+ * 根据时间推断格式化字符串
6959
+ * @param value
6960
+ * @param isUTC
6961
+ * @returns
6962
+ */
5613
6963
  function getFormatFromValue(value, isUTC) {
5614
6964
  const date = toDate(value);
5615
6965
  const M = date[monthGetterName(isUTC)]() + 1;
@@ -5657,15 +7007,20 @@
5657
7007
  const m = date[minutesGetterName(isUTC)]();
5658
7008
  const s = date[secondsGetterName(isUTC)]();
5659
7009
  const S = date[millisecondsGetterName(isUTC)]();
7010
+ // 月份,周等需要管理管理locale的时间格式化暂时不支持
5660
7011
  return ((template || '')
5661
7012
  .replace(/YYYY/g, pad(y + '', 4, '0', 'left'))
5662
7013
  .replace(/yyyy/g, y + '')
5663
7014
  .replace(/yy/g, (y % 100) + '')
5664
7015
  .replace(/Q/g, q + '')
7016
+ // .replace(/{MMMM}/g, month[M - 1])
7017
+ // .replace(/{MMM}/g, monthAbbr[M - 1])
5665
7018
  .replace(/MM/g, pad(M, 2, '0', 'left'))
5666
7019
  .replace(/M/g, M + '')
5667
7020
  .replace(/dd/g, pad(d, 2, '0', 'left'))
5668
7021
  .replace(/d/g, d + '')
7022
+ // .replace(/{eeee}/g, dayOfWeek[e])
7023
+ // .replace(/{ee}/g, dayOfWeekAbbr[e])
5669
7024
  .replace(/e/g, e + '')
5670
7025
  .replace(/HH/g, pad(H, 2, '0', 'left'))
5671
7026
  .replace(/H/g, H + '')
@@ -5967,9 +7322,14 @@
5967
7322
  };
5968
7323
  }
5969
7324
  const style = getComputedStyle(el);
7325
+ // clientWidth/clientHeight: 默认整数,会向上取整,导致canvas > container
7326
+ // getBoundingClientRect:默认小数,但是在container上有css类似 transform: scale(0.5)时,获取结果不对
7327
+ // getComputedStyle:默认小数,获取最终结果,但是会包含padding;
5970
7328
  if (/^(\d*\.?\d+)(px)$/.exec(style.width)) {
7329
+ // 当dom元素的 display: 'none' 的时候,获取到的宽高会变成原始的样式配置,而不是计算后的像素值
5971
7330
  const computedWidth = parseFloat(style.width) - parseFloat(style.paddingLeft) - parseFloat(style.paddingRight) || el.clientWidth - 1;
5972
7331
  const computedHeight = parseFloat(style.height) - parseFloat(style.paddingTop) - parseFloat(style.paddingBottom) || el.clientHeight - 1;
7332
+ // 理论上不用向下取整,目前没加。
5973
7333
  return {
5974
7334
  width: computedWidth <= 0 ? defaultWidth : computedWidth,
5975
7335
  height: computedHeight <= 0 ? defaultHeight : computedHeight
@@ -5977,10 +7337,20 @@
5977
7337
  }
5978
7338
  return { width: defaultWidth, height: defaultHeight };
5979
7339
  }
7340
+ /**
7341
+ * 获取元素的绝对位置坐标(相对于页面左上角)
7342
+ * @param element
7343
+ * @returns
7344
+ */
5980
7345
  function getElementAbsolutePosition(element) {
5981
7346
  const { x, y } = element.getBoundingClientRect();
5982
7347
  return { x, y };
5983
7348
  }
7349
+ /**
7350
+ * 获取元素的相对位置坐标(相对于其他dom元素)
7351
+ * @param element
7352
+ * @returns
7353
+ */
5984
7354
  function getElementRelativePosition(element, base) {
5985
7355
  const posElement = getElementAbsolutePosition(element);
5986
7356
  const posBase = getElementAbsolutePosition(base);
@@ -6012,12 +7382,23 @@
6012
7382
  const getScaleY = (element) => {
6013
7383
  return element.getBoundingClientRect().height / element.offsetHeight;
6014
7384
  };
7385
+ /**
7386
+ * 获取目标元素的缩放因数
7387
+ * @param element 目标 dom 元素
7388
+ * @returns
7389
+ */
6015
7390
  const getScale = (element) => {
6016
7391
  if (element.offsetWidth > 0) {
6017
7392
  return getScaleX(element);
6018
7393
  }
6019
7394
  return getScaleY(element);
6020
7395
  };
7396
+ /**
7397
+ * 判断是否是元素的父元素
7398
+ * @param element 从其父元素开始查找的 DOM 元素
7399
+ * @param target 要查找的目标父元素
7400
+ * @returns boolean, true 代表查找到,false 表示未找到
7401
+ */
6021
7402
  function hasParentElement(element, target) {
6022
7403
  let parent = element.parentNode;
6023
7404
  while (parent !== null) {
@@ -6028,6 +7409,11 @@
6028
7409
  }
6029
7410
  return false;
6030
7411
  }
7412
+ /**
7413
+ * 将style字符串转换成对象形式
7414
+ * @param style字符串 例如:"color:red;line-height:20px;"
7415
+ * @returns style对象,如 { color: 'red', 'line-height': '20px' }
7416
+ */
6031
7417
  const styleStringToObject = (styleStr = '') => {
6032
7418
  const res = {};
6033
7419
  styleStr.split(';').forEach(item => {
@@ -6044,9 +7430,19 @@
6044
7430
  });
6045
7431
  return res;
6046
7432
  };
7433
+ /**
7434
+ * 将小驼峰转换成中划线连接的字符串
7435
+ * @param str 如:'lineHeight' => 'line-height'
7436
+ * @returns
7437
+ */
6047
7438
  const lowerCamelCaseToMiddle = (str) => {
6048
7439
  return str.replace(/([A-Z])/g, '-$1').toLowerCase();
6049
7440
  };
7441
+ /**
7442
+ * 将中划线连接的字符串转换成驼峰字符串
7443
+ * @param str 如:'line-height' => 'lineHeight'
7444
+ * @returns
7445
+ */
6050
7446
  function toCamelCase(str) {
6051
7447
  return str.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
6052
7448
  }
@@ -6055,6 +7451,7 @@
6055
7451
  return obj instanceof Element;
6056
7452
  }
6057
7453
  catch (_a) {
7454
+ // 跨端 plan B
6058
7455
  const htmlElementKeys = [
6059
7456
  'children',
6060
7457
  'innerHTML',
@@ -6122,6 +7519,29 @@
6122
7519
  return distance / factor;
6123
7520
  }
6124
7521
 
7522
+ /**
7523
+ * The MIT License (MIT)
7524
+ *
7525
+ * Copyright (c) 2019 Morgan Herlocker
7526
+ *
7527
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
7528
+ * this software and associated documentation files (the "Software"), to deal in
7529
+ * the Software without restriction, including without limitation the rights to
7530
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7531
+ * the Software, and to permit persons to whom the Software is furnished to do so,
7532
+ * subject to the following conditions:
7533
+ *
7534
+ * The above copyright notice and this permission notice shall be included in allcopies or substantial portions of the Software.
7535
+ *
7536
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
7537
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
7538
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
7539
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
7540
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
7541
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7542
+ *
7543
+ */
7544
+ // https://github.com/Turfjs/turf
6125
7545
  function getGeom(geojson) {
6126
7546
  if (geojson.type === 'Feature') {
6127
7547
  return geojson.geometry;
@@ -6137,6 +7557,7 @@
6137
7557
  }
6138
7558
  const geom = getGeom(polygon);
6139
7559
  const type = geom.type;
7560
+ // BBox [west, south, east, north]
6140
7561
  const bbox = polygon.bbox;
6141
7562
  let polys = geom.coordinates;
6142
7563
  if (bbox && pointInRect(point, { x1: bbox[0], x2: bbox[1], y1: bbox[1], y2: bbox[3] }, true) === true) {
@@ -6157,11 +7578,13 @@
6157
7578
  }
6158
7579
  return result;
6159
7580
  }
7581
+ // https://github.com/Turfjs/turf
6160
7582
  function destination(point, distance, bearing, options = {}) {
6161
7583
  const longitude1 = degreeToRadian(point[0]);
6162
7584
  const latitude1 = degreeToRadian(point[1]);
6163
7585
  const bearingRad = degreeToRadian(bearing);
6164
7586
  const radians = lengthToRadians(distance, options.units);
7587
+ // Main
6165
7588
  const latitude2 = Math.asin(Math.sin(latitude1) * Math.cos(radians) + Math.cos(latitude1) * Math.sin(radians) * Math.cos(bearingRad));
6166
7589
  const longitude2 = longitude1 +
6167
7590
  Math.atan2(Math.sin(bearingRad) * Math.sin(radians) * Math.cos(latitude1), Math.cos(radians) - Math.sin(latitude1) * Math.sin(latitude2));
@@ -6170,17 +7593,41 @@
6170
7593
  return { x: lng, y: lat };
6171
7594
  }
6172
7595
 
7596
+ /* Adapted from venn.js by Ben Frederickson
7597
+ * https://github.com/benfred/venn.js
7598
+ * Licensed under the MIT
7599
+
7600
+ * url: https://github.com/benfred/venn.js/blob/master/src/circleintersection.js
7601
+ * License: https://github.com/benfred/venn.js/blob/master/LICENSE
7602
+ * @license
7603
+ */
6173
7604
  const SMALL = 1e-10;
6174
7605
 
7606
+ /* Adapted from venn.js by Ben Frederickson
7607
+ * https://github.com/benfred/venn.js
7608
+ * Licensed under the MIT
7609
+
7610
+ * url: https://github.com/benfred/venn.js/blob/master/src/circleintersection.js
7611
+ * License: https://github.com/benfred/venn.js/blob/master/LICENSE
7612
+ * @license
7613
+ */
7614
+ /** Returns the intersection area of a bunch of circles (where each circle
7615
+ is an object having an x,y and radius property) */
6175
7616
  function intersectionArea(circles, stats) {
7617
+ // get all the intersection points of the circles
6176
7618
  const intersectionPoints = getIntersectionPoints(circles);
7619
+ // filter out points that aren't included in all the circles
6177
7620
  const innerPoints = intersectionPoints.filter(function (p) {
6178
7621
  return containedInCircles(p, circles);
6179
7622
  });
6180
7623
  let arcArea = 0;
6181
7624
  let polygonArea = 0;
6182
7625
  const arcs = [];
7626
+ // if we have intersection points that are within all the circles,
7627
+ // then figure out the area contained by them
6183
7628
  if (innerPoints.length > 1) {
7629
+ // sort the points by angle from the center of the polygon, which lets
7630
+ // us just iterate over points to get the edges
6184
7631
  const center = getCenter(innerPoints);
6185
7632
  for (let i = 0; i < innerPoints.length; ++i) {
6186
7633
  const p = innerPoints[i];
@@ -6189,14 +7636,20 @@
6189
7636
  innerPoints.sort(function (a, b) {
6190
7637
  return b.angle - a.angle;
6191
7638
  });
7639
+ // iterate over all points, get arc between the points
7640
+ // and update the areas
6192
7641
  let p2 = innerPoints[innerPoints.length - 1];
6193
7642
  for (let i = 0; i < innerPoints.length; ++i) {
6194
7643
  const p1 = innerPoints[i];
7644
+ // polygon area updates easily ...
6195
7645
  polygonArea += (p2.x + p1.x) * (p1.y - p2.y);
7646
+ // updating the arc area is a little more involved
6196
7647
  const midPoint = { x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2 };
6197
7648
  let arc = null;
6198
7649
  for (let j = 0; j < p1.parentIndex.length; ++j) {
6199
7650
  if (p2.parentIndex.indexOf(p1.parentIndex[j]) > -1) {
7651
+ // figure out the angle halfway between the two points
7652
+ // on the current circle
6200
7653
  const circle = circles[p1.parentIndex[j]];
6201
7654
  const a1 = Math.atan2(p1.x - circle.x, p1.y - circle.y);
6202
7655
  const a2 = Math.atan2(p2.x - circle.x, p2.y - circle.y);
@@ -6204,14 +7657,19 @@
6204
7657
  if (angleDiff < 0) {
6205
7658
  angleDiff += 2 * Math.PI;
6206
7659
  }
7660
+ // and use that angle to figure out the width of the
7661
+ // arc
6207
7662
  const a = a2 - angleDiff / 2;
6208
7663
  let width = PointService.distancePP(midPoint, {
6209
7664
  x: circle.x + circle.radius * Math.sin(a),
6210
7665
  y: circle.y + circle.radius * Math.cos(a)
6211
7666
  });
7667
+ // clamp the width to the largest is can actually be
7668
+ // (sometimes slightly overflows because of FP errors)
6212
7669
  if (width > circle.radius * 2) {
6213
7670
  width = circle.radius * 2;
6214
7671
  }
7672
+ // pick the circle whose arc has the smallest width
6215
7673
  if (arc === null || arc.width > width) {
6216
7674
  arc = { circle: circle, width: width, p1: p1, p2: p2 };
6217
7675
  }
@@ -6225,12 +7683,16 @@
6225
7683
  }
6226
7684
  }
6227
7685
  else {
7686
+ // no intersection points, is either disjoint - or is completely
7687
+ // overlapped. figure out which by examining the smallest circle
6228
7688
  let smallest = circles[0];
6229
7689
  for (let i = 1; i < circles.length; ++i) {
6230
7690
  if (circles[i].radius < smallest.radius) {
6231
7691
  smallest = circles[i];
6232
7692
  }
6233
7693
  }
7694
+ // make sure the smallest circle is completely contained in all
7695
+ // the other circles
6234
7696
  let disjoint = false;
6235
7697
  for (let i = 0; i < circles.length; ++i) {
6236
7698
  if (PointService.distancePP(circles[i], smallest) > Math.abs(smallest.radius - circles[i].radius)) {
@@ -6262,6 +7724,7 @@
6262
7724
  }
6263
7725
  return arcArea + polygonArea;
6264
7726
  }
7727
+ /** returns whether a point is contained by all of a list of circles */
6265
7728
  function containedInCircles(point, circles) {
6266
7729
  for (let i = 0; i < circles.length; ++i) {
6267
7730
  if (PointService.distancePP(point, circles[i]) > circles[i].radius + SMALL) {
@@ -6270,6 +7733,7 @@
6270
7733
  }
6271
7734
  return true;
6272
7735
  }
7736
+ /** Gets all intersection points between a bunch of circles */
6273
7737
  function getIntersectionPoints(circles) {
6274
7738
  const ret = [];
6275
7739
  for (let i = 0; i < circles.length; ++i) {
@@ -6284,13 +7748,19 @@
6284
7748
  }
6285
7749
  return ret;
6286
7750
  }
7751
+ /** Circular segment area calculation. See http://mathworld.wolfram.com/CircularSegment.html */
6287
7752
  function circleArea(r, width) {
6288
7753
  return r * r * Math.acos(1 - width / r) - (r - width) * Math.sqrt(width * (2 * r - width));
6289
7754
  }
7755
+ /** Returns the overlap area of two circles of radius r1 and r2 - that
7756
+ have their centers separated by distance d. Simpler faster
7757
+ circle intersection for only two circles */
6290
7758
  function circleOverlap(r1, r2, d) {
7759
+ // no overlap
6291
7760
  if (d >= r1 + r2) {
6292
7761
  return 0;
6293
7762
  }
7763
+ // completely overlapped
6294
7764
  if (d <= Math.abs(r1 - r2)) {
6295
7765
  return Math.PI * Math.min(r1, r2) * Math.min(r1, r2);
6296
7766
  }
@@ -6298,10 +7768,15 @@
6298
7768
  const w2 = r2 - (d * d - r1 * r1 + r2 * r2) / (2 * d);
6299
7769
  return circleArea(r1, w1) + circleArea(r2, w2);
6300
7770
  }
7771
+ /** Given two circles (containing a x/y/radius attributes),
7772
+ returns the intersecting points if possible.
7773
+ note: doesn't handle cases where there are infinitely many
7774
+ intersection points (circles are equivalent):, or only one intersection point*/
6301
7775
  function circleCircleIntersection(p1, p2) {
6302
7776
  const d = PointService.distancePP(p1, p2);
6303
7777
  const r1 = p1.radius;
6304
7778
  const r2 = p2.radius;
7779
+ // if to far away, or self contained - can't be done
6305
7780
  if (d >= r1 + r2 || d <= Math.abs(r1 - r2)) {
6306
7781
  return [];
6307
7782
  }
@@ -6316,6 +7791,7 @@
6316
7791
  { x: x0 - rx, y: y0 + ry }
6317
7792
  ];
6318
7793
  }
7794
+ /** Returns the center of a bunch of points */
6319
7795
  function getCenter(points) {
6320
7796
  const center = { x: 0, y: 0 };
6321
7797
  for (let i = 0; i < points.length; ++i) {
@@ -6327,6 +7803,14 @@
6327
7803
  return center;
6328
7804
  }
6329
7805
 
7806
+ /* Adapted from d3-time-format by Mike Bostock
7807
+ * https://github.com/d3/d3-time-format
7808
+ * Licensed under the ISC
7809
+
7810
+ * url: https://github.com/d3/d3-time-format/blob/d6feb945baa4b7a45898a9ca926ebca044e20657/src/locale.js
7811
+ * License: https://github.com/d3/d3-time-format/blob/main/LICENSE
7812
+ * @license
7813
+ */
6330
7814
  class TimeUtil {
6331
7815
  static getInstance() {
6332
7816
  if (!TimeUtil.instance) {
@@ -6339,8 +7823,9 @@
6339
7823
  this.locale_periods = ['AM', 'PM'];
6340
7824
  this.locale_weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
6341
7825
  this.locale_shortMonths = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
6342
- this.numberRe = /^\s*\d+/;
7826
+ this.numberRe = /^\s*\d+/; // note: ignores next directive
6343
7827
  this.pads = { '-': '', _: ' ', '0': '0' };
7828
+ // const percentRe = /^%/;
6344
7829
  this.requoteRe = /[\\^$*+?|[\]().{}]/g;
6345
7830
  this.locale_months = [
6346
7831
  'January',
@@ -6617,18 +8102,22 @@
6617
8102
  if (i !== string.length) {
6618
8103
  return null;
6619
8104
  }
8105
+ // If a UNIX timestamp is specified, return it.
6620
8106
  if ('Q' in d) {
6621
8107
  return new Date(d.Q);
6622
8108
  }
6623
8109
  if ('s' in d) {
6624
8110
  return new Date(d.s * 1000 + ('L' in d ? d.L : 0));
6625
8111
  }
8112
+ // If this is utcParse, never use the local timezone.
6626
8113
  if (Z && !('Z' in d)) {
6627
8114
  d.Z = 0;
6628
8115
  }
8116
+ // The am-pm flag is 0 for AM, and 1 for PM.
6629
8117
  if ('p' in d) {
6630
8118
  d.H = (d.H % 12) + d.p * 12;
6631
8119
  }
8120
+ // If the month was not specified, inherit from the quarter.
6632
8121
  if (d.m === undefined) {
6633
8122
  d.m = 'q' in d ? d.q : 0;
6634
8123
  }
@@ -6637,6 +8126,7 @@
6637
8126
  d.M += d.Z % 100;
6638
8127
  return that.utcDate(d);
6639
8128
  }
8129
+ // Otherwise, all fields are in local time.
6640
8130
  return that.localDate(d);
6641
8131
  };
6642
8132
  }
@@ -6678,6 +8168,13 @@
6678
8168
  }
6679
8169
  }
6680
8170
 
8171
+ /* Adapted from d3-time-format by Mike Bostock
8172
+ * https://github.com/d3/d3-format
8173
+ * Licensed under the ISC
8174
+
8175
+ * License: https://github.com/d3/d3-format/blob/main/LICENSE
8176
+ * @license
8177
+ */
6681
8178
  function formatDecimal(x) {
6682
8179
  return Math.abs((x = Math.round(x))) >= 1e21 ? x.toLocaleString('en').replace(/,/g, '') : x.toString(10);
6683
8180
  }
@@ -6685,12 +8182,19 @@
6685
8182
  const _x = p ? x.toExponential(p - 1) : x.toExponential();
6686
8183
  const i = _x.indexOf('e');
6687
8184
  if (i < 0) {
6688
- return null;
8185
+ return null; // NaN, ±Infinity
6689
8186
  }
6690
8187
  const coefficient = _x.slice(0, i);
6691
8188
  return [coefficient.length > 1 ? coefficient[0] + coefficient.slice(2) : coefficient, +_x.slice(i + 1)];
6692
8189
  }
6693
8190
 
8191
+ /* Adapted from d3-time-format by Mike Bostock
8192
+ * https://github.com/d3/d3-format
8193
+ * Licensed under the ISC
8194
+
8195
+ * License: https://github.com/d3/d3-format/blob/main/LICENSE
8196
+ * @license
8197
+ */
6694
8198
  function formatGroup(grouping, thousands) {
6695
8199
  return function (value, width) {
6696
8200
  let i = value.length;
@@ -6712,6 +8216,13 @@
6712
8216
  };
6713
8217
  }
6714
8218
 
8219
+ /* Adapted from d3-time-format by Mike Bostock
8220
+ * https://github.com/d3/d3-format
8221
+ * Licensed under the ISC
8222
+
8223
+ * License: https://github.com/d3/d3-format/blob/main/LICENSE
8224
+ * @license
8225
+ */
6715
8226
  let prefixExponent;
6716
8227
  function formatPrefixAuto(x, p) {
6717
8228
  const d = formatDecimalParts(x, p);
@@ -6728,9 +8239,16 @@
6728
8239
  ? coefficient + new Array(i - n + 1).join('0')
6729
8240
  : i > 0
6730
8241
  ? coefficient.slice(0, i) + '.' + coefficient.slice(i)
6731
- : '0.' + new Array(1 - i).join('0') + formatDecimalParts(x, Math.max(0, p + i - 1))[0];
8242
+ : '0.' + new Array(1 - i).join('0') + formatDecimalParts(x, Math.max(0, p + i - 1))[0]; // less than 1y!
6732
8243
  }
6733
8244
 
8245
+ /* Adapted from d3-time-format by Mike Bostock
8246
+ * https://github.com/d3/d3-format
8247
+ * Licensed under the ISC
8248
+
8249
+ * License: https://github.com/d3/d3-format/blob/main/LICENSE
8250
+ * @license
8251
+ */
6734
8252
  function formatRounded(x, p) {
6735
8253
  const d = formatDecimalParts(x, p);
6736
8254
  if (!d) {
@@ -6745,6 +8263,13 @@
6745
8263
  : coefficient + new Array(exponent - coefficient.length + 2).join('0');
6746
8264
  }
6747
8265
 
8266
+ /* Adapted from d3-time-format by Mike Bostock
8267
+ * https://github.com/d3/d3-format
8268
+ * Licensed under the ISC
8269
+
8270
+ * License: https://github.com/d3/d3-format/blob/main/LICENSE
8271
+ * @license
8272
+ */
6748
8273
  function formatTrim(s) {
6749
8274
  const n = s.length;
6750
8275
  let i0 = -1;
@@ -6820,6 +8345,13 @@
6820
8345
  });
6821
8346
  }
6822
8347
 
8348
+ /* Adapted from d3-time-format by Mike Bostock
8349
+ * https://github.com/d3/d3-format
8350
+ * Licensed under the ISC
8351
+
8352
+ * License: https://github.com/d3/d3-format/blob/main/LICENSE
8353
+ * @license
8354
+ */
6823
8355
  const prefixes = ['y', 'z', 'a', 'f', 'p', 'n', 'µ', 'm', '', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
6824
8356
  class NumberUtil {
6825
8357
  constructor() {
@@ -6868,19 +8400,33 @@
6868
8400
  let precision = specifierIns.precision;
6869
8401
  let trim = specifierIns.trim;
6870
8402
  let type = specifierIns.type;
8403
+ // The "n" type is an alias for ",g".
6871
8404
  if (type === 'n') {
6872
8405
  (comma = true), (type = 'g');
6873
8406
  }
8407
+ // The "" type, and any invalid type, is an alias for ".12~g".
8408
+ // @ts-ignore
6874
8409
  else if (!formatTypes[type]) {
6875
8410
  precision === undefined && (precision = 12), (trim = true), (type = 'g');
6876
8411
  }
8412
+ // If zero fill is specified, padding goes after sign and before digits.
8413
+ // @ts-ignore
6877
8414
  if (zero || (fill === '0' && align === '=')) {
6878
8415
  (zero = true), (fill = '0'), (align = '=');
6879
8416
  }
8417
+ // Compute the prefix and suffix.
8418
+ // For SI-prefix, the suffix is lazily computed.
6880
8419
  const prefix = symbol === '$' ? this.currencyPrefix : symbol === '#' && /[boxX]/.test(type) ? '0' + type.toLowerCase() : '';
6881
8420
  const suffix = symbol === '$' ? this.currencySuffix : /[%p]/.test(type) ? this.percent : '';
8421
+ // What format function should we use?
8422
+ // Is this an integer type?
8423
+ // Can this type generate exponential notation?
6882
8424
  const formatType = formatTypes[type];
6883
8425
  const maybeSuffix = /[defgprstz%]/.test(type);
8426
+ // Set the default precision if not specified,
8427
+ // or clamp the specified precision to the supported range.
8428
+ // For significant precision, it must be in [1, 21].
8429
+ // For fixed precision, it must be in [0, 20].
6884
8430
  precision =
6885
8431
  precision === undefined
6886
8432
  ? 6
@@ -6901,20 +8447,27 @@
6901
8447
  }
6902
8448
  else {
6903
8449
  _value = +_value;
8450
+ // Determine the sign. -0 is not less than 0, but 1 / -0 is!
6904
8451
  let valueNegative = _value < 0 || 1 / _value < 0;
8452
+ // Perform the initial formatting.
6905
8453
  _value = isNaN(_value) ? nan : formatType(Math.abs(_value), precision);
8454
+ // Trim insignificant zeros.
6906
8455
  if (trim) {
6907
8456
  _value = formatTrim(_value);
6908
8457
  }
8458
+ // If a negative value rounds to zero after formatting, and no explicit positive sign is requested, hide the sign.
6909
8459
  if (valueNegative && +_value === 0 && sign !== '+') {
6910
8460
  valueNegative = false;
6911
8461
  }
8462
+ // Compute the prefix and suffix.
6912
8463
  valuePrefix =
6913
8464
  (valueNegative ? (sign === '(' ? sign : minus) : sign === '-' || sign === '(' ? '' : sign) + valuePrefix;
6914
8465
  valueSuffix =
6915
8466
  (type === 's' ? prefixes[8 + prefixExponent / 3] : '') +
6916
8467
  valueSuffix +
6917
8468
  (valueNegative && sign === '(' ? ')' : '');
8469
+ // Break the formatted value into the integer “value” part that can be
8470
+ // grouped, and fractional or exponential “suffix” part that is not.
6918
8471
  if (maybeSuffix) {
6919
8472
  (i = -1), (n = _value.length);
6920
8473
  while (++i < n) {
@@ -6926,15 +8479,19 @@
6926
8479
  }
6927
8480
  }
6928
8481
  }
8482
+ // If the fill character is not "0", grouping is applied before padding.
6929
8483
  if (comma && !zero) {
6930
8484
  _value = group(_value, Infinity);
6931
8485
  }
8486
+ // Compute the padding.
6932
8487
  let length = valuePrefix.length + _value.length + valueSuffix.length;
6933
8488
  let padding = length < width ? new Array(width - length + 1).join(fill) : '';
8489
+ // If the fill character is "0", grouping is applied after padding.
6934
8490
  if (comma && zero) {
6935
8491
  _value = group(padding + _value, padding.length ? width - valueSuffix.length : Infinity);
6936
8492
  padding = '';
6937
8493
  }
8494
+ // Reconstruct the final output based on the desired alignment.
6938
8495
  switch (align) {
6939
8496
  case '<':
6940
8497
  _value = valuePrefix + _value + valueSuffix + padding;
@@ -6988,6 +8545,7 @@
6988
8545
  X: (x) => Math.round(x).toString(16).toUpperCase(),
6989
8546
  x: (x) => Math.round(x).toString(16),
6990
8547
  t: (x, p) => {
8548
+ // 判断是否为整数
6991
8549
  if (Number.isInteger(x)) {
6992
8550
  return x.toFixed(2);
6993
8551
  }
@@ -7005,6 +8563,16 @@
7005
8563
  };
7006
8564
  }
7007
8565
 
8566
+ /* Adapted from fmin by Ben Frederickson
8567
+ * https://github.com/benfred/fmin
8568
+ * Licensed under the BSD-3-Clause
8569
+
8570
+ * url: https://github.com/benfred/fmin/blob/master/src/blas1.js
8571
+ * License: https://github.com/benfred/fmin/blob/master/LICENSE
8572
+ * @license
8573
+ */
8574
+ // need some basic operations on vectors, rather than adding a dependency,
8575
+ // just define here
7008
8576
  function zeros(x) {
7009
8577
  const r = new Array(x);
7010
8578
  for (let i = 0; i < x; ++i) {
@@ -7036,6 +8604,15 @@
7036
8604
  }
7037
8605
  }
7038
8606
 
8607
+ /* Adapted from fmin by Ben Frederickson
8608
+ * https://github.com/benfred/fmin
8609
+ * Licensed under the BSD-3-Clause
8610
+
8611
+ * url: https://github.com/benfred/fmin/blob/master/src/nelderMead.js
8612
+ * License: https://github.com/benfred/fmin/blob/master/LICENSE
8613
+ * @license
8614
+ */
8615
+ /** minimizes a function using the downhill simplex method */
7039
8616
  function nelderMead(f, x0, parameters) {
7040
8617
  parameters = parameters || {};
7041
8618
  const maxIterations = parameters.maxIterations || x0.length * 200;
@@ -7048,6 +8625,7 @@
7048
8625
  const psi = parameters.psi !== undefined ? parameters.psi : -0.5;
7049
8626
  const sigma = parameters.sigma !== undefined ? parameters.sigma : 0.5;
7050
8627
  let maxDiff;
8628
+ // initialize simplex.
7051
8629
  const N = x0.length;
7052
8630
  const simplex = new Array(N + 1);
7053
8631
  simplex[0] = x0;
@@ -7076,6 +8654,8 @@
7076
8654
  for (let iteration = 0; iteration < maxIterations; ++iteration) {
7077
8655
  simplex.sort(sortOrder);
7078
8656
  if (parameters.history) {
8657
+ // copy the simplex (since later iterations will mutate) and
8658
+ // sort it to have a consistent order between iterations
7079
8659
  const sortedSimplex = simplex.map(function (x) {
7080
8660
  const state = x.slice();
7081
8661
  state.fx = x.fx;
@@ -7094,6 +8674,7 @@
7094
8674
  if (Math.abs(simplex[0].fx - simplex[N].fx) < minErrorDelta && maxDiff < minTolerance) {
7095
8675
  break;
7096
8676
  }
8677
+ // compute the centroid of all but the worst point in the simplex
7097
8678
  for (let i = 0; i < N; ++i) {
7098
8679
  centroid[i] = 0;
7099
8680
  for (let j = 0; j < N; ++j) {
@@ -7101,9 +8682,12 @@
7101
8682
  }
7102
8683
  centroid[i] /= N;
7103
8684
  }
8685
+ // reflect the worst point past the centroid and compute loss at reflected
8686
+ // point
7104
8687
  const worst = simplex[N];
7105
8688
  weightedSum(reflected, 1 + rho, centroid, -rho, worst);
7106
8689
  reflected.fx = f(reflected);
8690
+ // if the reflected point is the best seen, then possibly expand
7107
8691
  if (reflected.fx < simplex[0].fx) {
7108
8692
  weightedSum(expanded, 1 + chi, centroid, -chi, worst);
7109
8693
  expanded.fx = f(expanded);
@@ -7114,9 +8698,12 @@
7114
8698
  updateSimplex(reflected);
7115
8699
  }
7116
8700
  }
8701
+ // if the reflected point is worse than the second worst, we need to
8702
+ // contract
7117
8703
  else if (reflected.fx >= simplex[N - 1].fx) {
7118
8704
  let shouldReduce = false;
7119
8705
  if (reflected.fx > worst.fx) {
8706
+ // do an inside contraction
7120
8707
  weightedSum(contracted, 1 + psi, centroid, -psi, worst);
7121
8708
  contracted.fx = f(contracted);
7122
8709
  if (contracted.fx < worst.fx) {
@@ -7127,6 +8714,7 @@
7127
8714
  }
7128
8715
  }
7129
8716
  else {
8717
+ // do an outside contraction
7130
8718
  weightedSum(contracted, 1 - psi * rho, centroid, psi * rho, worst);
7131
8719
  contracted.fx = f(contracted);
7132
8720
  if (contracted.fx < reflected.fx) {
@@ -7137,9 +8725,11 @@
7137
8725
  }
7138
8726
  }
7139
8727
  if (shouldReduce) {
8728
+ // if we don't contract here, we're done
7140
8729
  if (sigma >= 1) {
7141
8730
  break;
7142
8731
  }
8732
+ // do a reduction
7143
8733
  for (let i = 1; i < simplex.length; ++i) {
7144
8734
  weightedSum(simplex[i], 1 - sigma, simplex[0], sigma, simplex[i]);
7145
8735
  simplex[i].fx = f(simplex[i]);
@@ -7154,6 +8744,21 @@
7154
8744
  return { fx: simplex[0].fx, x: simplex[0] };
7155
8745
  }
7156
8746
 
8747
+ /* Adapted from fmin by Ben Frederickson
8748
+ * https://github.com/benfred/fmin
8749
+ * Licensed under the BSD-3-Clause
8750
+
8751
+ * url: https://github.com/benfred/fmin/blob/master/src/linesearch.js
8752
+ * License: https://github.com/benfred/fmin/blob/master/LICENSE
8753
+ * @license
8754
+ */
8755
+ /// searches along line 'pk' for a point that satifies the wolfe conditions
8756
+ /// See 'Numerical Optimization' by Nocedal and Wright p59-60
8757
+ /// f : objective function
8758
+ /// pk : search direction
8759
+ /// current: object containing current gradient/loss
8760
+ /// next: output: contains next gradient/loss
8761
+ /// returns a: step size taken
7157
8762
  function wolfeLineSearch(f, pk, current, next, a, c1, c2) {
7158
8763
  const phi0 = current.fx;
7159
8764
  const phiPrime0 = dotProduct(current.fxprime, pk);
@@ -7206,7 +8811,17 @@
7206
8811
  return a;
7207
8812
  }
7208
8813
 
8814
+ /* Adapted from fmin by Ben Frederickson
8815
+ * https://github.com/benfred/fmin
8816
+ * Licensed under the BSD-3-Clause
8817
+
8818
+ * url: https://github.com/benfred/fmin/blob/master/src/conjugateGradient.js
8819
+ * License: https://github.com/benfred/fmin/blob/master/LICENSE
8820
+ * @license
8821
+ */
7209
8822
  function conjugateGradient(f, initial, params) {
8823
+ // allocate all memory up front here, keep out of the loop for perfomance
8824
+ // reasons
7210
8825
  let current = { x: initial.slice(), fx: 0, fxprime: initial.slice() };
7211
8826
  let next = { x: initial.slice(), fx: 0, fxprime: initial.slice() };
7212
8827
  const yk = initial.slice();
@@ -7219,13 +8834,17 @@
7219
8834
  scale(pk, current.fxprime, -1);
7220
8835
  for (let i = 0; i < maxIterations; ++i) {
7221
8836
  a = wolfeLineSearch(f, pk, current, next, a);
8837
+ // todo: history in wrong spot?
7222
8838
  if (params.history) {
7223
8839
  params.history.push({ x: current.x.slice(), fx: current.fx, fxprime: current.fxprime.slice(), alpha: a });
7224
8840
  }
7225
8841
  if (!a) {
8842
+ // faiiled to find point that satifies wolfe conditions.
8843
+ // reset direction for next iteration
7226
8844
  scale(pk, current.fxprime, -1);
7227
8845
  }
7228
8846
  else {
8847
+ // update direction using Polak–Ribiere CG method
7229
8848
  weightedSum(yk, 1, next.fxprime, -1, current.fxprime);
7230
8849
  const delta_k = dotProduct(current.fxprime, current.fxprime);
7231
8850
  const beta_k = Math.max(0, dotProduct(yk, next.fxprime) / delta_k);
@@ -7288,6 +8907,7 @@
7288
8907
  exports.binaryFuzzySearch = binaryFuzzySearch;
7289
8908
  exports.binaryFuzzySearchInNumberRange = binaryFuzzySearchInNumberRange;
7290
8909
  exports.bisect = bisect;
8910
+ exports.bootstrapEstimate = bootstrapEstimate;
7291
8911
  exports.calculateAnchorOfArc = calculateAnchorOfArc;
7292
8912
  exports.calculateAnchorOfBounds = calculateAnchorOfBounds;
7293
8913
  exports.calculateMaxRadius = calculateMaxRadius;
@@ -7308,6 +8928,7 @@
7308
8928
  exports.conjugateGradient = conjugateGradient;
7309
8929
  exports.constant = constant;
7310
8930
  exports.containedInCircles = containedInCircles;
8931
+ exports.correlation = correlation;
7311
8932
  exports.cos = cos;
7312
8933
  exports.crossProduct = crossProduct;
7313
8934
  exports.crossProductPoint = crossProductPoint;
@@ -7426,6 +9047,7 @@
7426
9047
  exports.lowerFirst = lowerFirst;
7427
9048
  exports.max = max;
7428
9049
  exports.maxInArray = maxInArray;
9050
+ exports.mean = mean;
7429
9051
  exports.median = median;
7430
9052
  exports.memoize = memoize;
7431
9053
  exports.merge = merge;
@@ -7452,6 +9074,7 @@
7452
9074
  exports.monthSetterName = monthSetterName;
7453
9075
  exports.nelderMead = nelderMead;
7454
9076
  exports.norm2 = norm2;
9077
+ exports.normalQuantile = normalQuantile;
7455
9078
  exports.normalTransform = normalTransform;
7456
9079
  exports.normalizeAngle = normalizeAngle;
7457
9080
  exports.normalizePadding = normalizePadding;
@@ -7482,6 +9105,7 @@
7482
9105
  exports.randomLCG = randomLCG;
7483
9106
  exports.range = range;
7484
9107
  exports.rectInsideAnotherRect = rectInsideAnotherRect;
9108
+ exports.regressionBootstrap = regressionBootstrap;
7485
9109
  exports.regressionLinear = regressionLinear;
7486
9110
  exports.regressionLogistic = regressionLogistic;
7487
9111
  exports.regressionLowess = regressionLowess;