@363045841yyt/klinechart-core 0.7.3 → 0.7.5-alpha.2

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 (231) hide show
  1. package/README.md +201 -201
  2. package/README.zh-CN.md +201 -201
  3. package/dist/engine/renderers/webgl/candleSurface.js +47 -47
  4. package/dist/version.d.ts +1 -1
  5. package/dist/version.d.ts.map +1 -1
  6. package/dist/version.js +1 -2
  7. package/dist/version.js.map +1 -1
  8. package/package.json +129 -122
  9. package/src/__tests__/signal.test.ts +124 -124
  10. package/src/config/chartSettings.ts +66 -66
  11. package/src/controllers/__tests__/drawing.test.ts +214 -214
  12. package/src/controllers/__tests__/indicatorSelector.test.ts +481 -481
  13. package/src/controllers/__tests__/toolbar.test.ts +225 -225
  14. package/src/controllers/createChartController.ts +665 -665
  15. package/src/controllers/createDrawingController.ts +96 -96
  16. package/src/controllers/createIndicatorSelectorController.ts +307 -307
  17. package/src/controllers/createToolbarController.ts +146 -146
  18. package/src/controllers/index.ts +19 -19
  19. package/src/controllers/types.ts +284 -284
  20. package/src/engine/__tests__/chart.dpr.test.ts +401 -401
  21. package/src/engine/__tests__/paneRenderer.resize.test.ts +92 -92
  22. package/src/engine/chart-store.ts +121 -121
  23. package/src/engine/chart.d.ts +617 -617
  24. package/src/engine/chart.ts +2815 -2815
  25. package/src/engine/controller/__tests__/interaction.dpr.test.ts +259 -259
  26. package/src/engine/controller/interaction.ts +722 -722
  27. package/src/engine/controller/markerInteraction.ts +130 -130
  28. package/src/engine/controller/pinchTracker.ts +82 -82
  29. package/src/engine/controller/tooltipPosition.ts +48 -48
  30. package/src/engine/draw/__tests__/pixelAlign.spec.ts +176 -176
  31. package/src/engine/draw/pixelAlign.ts +259 -259
  32. package/src/engine/drawing/index.ts +655 -655
  33. package/src/engine/drawing/interaction.ts +842 -842
  34. package/src/engine/drawing/plugin.ts +343 -343
  35. package/src/engine/indicators/__tests__/__fixtures__/golden/atr.json +38 -38
  36. package/src/engine/indicators/__tests__/__fixtures__/golden/dema.json +14 -14
  37. package/src/engine/indicators/__tests__/__fixtures__/golden/hma.json +14 -14
  38. package/src/engine/indicators/__tests__/__fixtures__/golden/index.ts +55 -55
  39. package/src/engine/indicators/__tests__/__fixtures__/golden/kama.json +14 -14
  40. package/src/engine/indicators/__tests__/__fixtures__/golden/tema.json +14 -14
  41. package/src/engine/indicators/__tests__/__fixtures__/golden/wma.json +40 -40
  42. package/src/engine/indicators/__tests__/__fixtures__/synthetic.ts +65 -65
  43. package/src/engine/indicators/__tests__/_propertyAssertions.ts +76 -76
  44. package/src/engine/indicators/__tests__/atr.test.ts +153 -153
  45. package/src/engine/indicators/__tests__/calculators.test.ts +614 -614
  46. package/src/engine/indicators/__tests__/cmf-mfi.test.ts +100 -100
  47. package/src/engine/indicators/__tests__/dema.test.ts +73 -73
  48. package/src/engine/indicators/__tests__/donchian.test.ts +70 -70
  49. package/src/engine/indicators/__tests__/hma.test.ts +73 -73
  50. package/src/engine/indicators/__tests__/ichimoku.test.ts +105 -105
  51. package/src/engine/indicators/__tests__/kama.test.ts +80 -80
  52. package/src/engine/indicators/__tests__/keltner.test.ts +65 -65
  53. package/src/engine/indicators/__tests__/pivot-fib.test.ts +110 -110
  54. package/src/engine/indicators/__tests__/roc.test.ts +68 -68
  55. package/src/engine/indicators/__tests__/sar.test.ts +86 -86
  56. package/src/engine/indicators/__tests__/scheduler.test.ts +831 -831
  57. package/src/engine/indicators/__tests__/soa.test.ts +533 -533
  58. package/src/engine/indicators/__tests__/structure.test.ts +110 -110
  59. package/src/engine/indicators/__tests__/supertrend.test.ts +65 -65
  60. package/src/engine/indicators/__tests__/tema.test.ts +68 -68
  61. package/src/engine/indicators/__tests__/trix.test.ts +70 -70
  62. package/src/engine/indicators/__tests__/volatility.test.ts +117 -117
  63. package/src/engine/indicators/__tests__/volume.test.ts +115 -115
  64. package/src/engine/indicators/__tests__/volumeProfile.test.ts +74 -74
  65. package/src/engine/indicators/__tests__/vwap.test.ts +69 -69
  66. package/src/engine/indicators/__tests__/wma.test.ts +112 -112
  67. package/src/engine/indicators/__tests__/zones.test.ts +95 -95
  68. package/src/engine/indicators/atrState.ts +27 -27
  69. package/src/engine/indicators/bollState.ts +51 -51
  70. package/src/engine/indicators/calculators.ts +2593 -2593
  71. package/src/engine/indicators/cciState.ts +25 -25
  72. package/src/engine/indicators/chaikinVolState.ts +32 -32
  73. package/src/engine/indicators/cmfState.ts +27 -27
  74. package/src/engine/indicators/demaState.ts +27 -27
  75. package/src/engine/indicators/donchianState.ts +43 -43
  76. package/src/engine/indicators/eneState.ts +43 -43
  77. package/src/engine/indicators/expmaState.ts +43 -43
  78. package/src/engine/indicators/fastkState.ts +25 -25
  79. package/src/engine/indicators/fibState.ts +41 -41
  80. package/src/engine/indicators/hmaState.ts +27 -27
  81. package/src/engine/indicators/hvState.ts +28 -28
  82. package/src/engine/indicators/ichimokuState.ts +70 -70
  83. package/src/engine/indicators/indicator.worker.ts +169 -169
  84. package/src/engine/indicators/indicatorDefinitionRegistry.ts +62 -62
  85. package/src/engine/indicators/indicatorMetadata.ts +110 -110
  86. package/src/engine/indicators/indicatorRegistry.ts +106 -106
  87. package/src/engine/indicators/indicatorRuntime.ts +1548 -1548
  88. package/src/engine/indicators/kamaState.ts +34 -34
  89. package/src/engine/indicators/keltnerState.ts +49 -49
  90. package/src/engine/indicators/kstState.ts +42 -42
  91. package/src/engine/indicators/maState.ts +36 -36
  92. package/src/engine/indicators/macdState.ts +76 -76
  93. package/src/engine/indicators/mfiState.ts +27 -27
  94. package/src/engine/indicators/momState.ts +25 -25
  95. package/src/engine/indicators/obvState.ts +25 -25
  96. package/src/engine/indicators/parkinsonState.ts +28 -28
  97. package/src/engine/indicators/pivotState.ts +51 -51
  98. package/src/engine/indicators/pvtState.ts +25 -25
  99. package/src/engine/indicators/rocState.ts +27 -27
  100. package/src/engine/indicators/rsiState.ts +65 -65
  101. package/src/engine/indicators/sarState.ts +41 -41
  102. package/src/engine/indicators/scheduler.ts +1205 -1205
  103. package/src/engine/indicators/soa.ts +352 -352
  104. package/src/engine/indicators/stateComposer.ts +1262 -1262
  105. package/src/engine/indicators/stochState.ts +26 -26
  106. package/src/engine/indicators/structureState.ts +69 -69
  107. package/src/engine/indicators/supertrendState.ts +37 -37
  108. package/src/engine/indicators/temaState.ts +27 -27
  109. package/src/engine/indicators/trixState.ts +35 -35
  110. package/src/engine/indicators/vmaState.ts +27 -27
  111. package/src/engine/indicators/volumeProfileState.ts +63 -63
  112. package/src/engine/indicators/vwapState.ts +29 -29
  113. package/src/engine/indicators/wmaState.ts +27 -27
  114. package/src/engine/indicators/wmsrState.ts +25 -25
  115. package/src/engine/indicators/workerProtocol.ts +613 -613
  116. package/src/engine/indicators/zonesState.ts +47 -47
  117. package/src/engine/layout/pane.ts +161 -161
  118. package/src/engine/marker/registry.ts +265 -265
  119. package/src/engine/paneRenderer.ts +169 -169
  120. package/src/engine/renderers/Indicator/atr.ts +237 -237
  121. package/src/engine/renderers/Indicator/boll.ts +317 -317
  122. package/src/engine/renderers/Indicator/cci.ts +275 -275
  123. package/src/engine/renderers/Indicator/chaikinVol.ts +138 -138
  124. package/src/engine/renderers/Indicator/cmf.ts +137 -137
  125. package/src/engine/renderers/Indicator/dema.ts +136 -136
  126. package/src/engine/renderers/Indicator/donchian.ts +137 -137
  127. package/src/engine/renderers/Indicator/ene.ts +271 -271
  128. package/src/engine/renderers/Indicator/expma.ts +197 -197
  129. package/src/engine/renderers/Indicator/fastk.ts +316 -316
  130. package/src/engine/renderers/Indicator/fib.ts +141 -141
  131. package/src/engine/renderers/Indicator/hma.ts +136 -136
  132. package/src/engine/renderers/Indicator/hv.ts +124 -124
  133. package/src/engine/renderers/Indicator/ichimoku.ts +181 -181
  134. package/src/engine/renderers/Indicator/index.ts +241 -241
  135. package/src/engine/renderers/Indicator/indicatorData.ts +650 -650
  136. package/src/engine/renderers/Indicator/kama.ts +136 -136
  137. package/src/engine/renderers/Indicator/keltner.ts +137 -137
  138. package/src/engine/renderers/Indicator/kst.ts +302 -302
  139. package/src/engine/renderers/Indicator/ma.ts +200 -200
  140. package/src/engine/renderers/Indicator/macd.ts +477 -477
  141. package/src/engine/renderers/Indicator/macdLegend.ts +141 -141
  142. package/src/engine/renderers/Indicator/mainIndicatorLegend.ts +272 -272
  143. package/src/engine/renderers/Indicator/mfi.ts +142 -142
  144. package/src/engine/renderers/Indicator/mom.ts +311 -311
  145. package/src/engine/renderers/Indicator/obv.ts +123 -123
  146. package/src/engine/renderers/Indicator/parkinson.ts +124 -124
  147. package/src/engine/renderers/Indicator/pivot.ts +131 -131
  148. package/src/engine/renderers/Indicator/pvt.ts +123 -123
  149. package/src/engine/renderers/Indicator/roc.ts +143 -143
  150. package/src/engine/renderers/Indicator/rsi.ts +390 -390
  151. package/src/engine/renderers/Indicator/sar.ts +113 -113
  152. package/src/engine/renderers/Indicator/scale/atr_scale.ts +19 -19
  153. package/src/engine/renderers/Indicator/scale/cci_scale.ts +19 -19
  154. package/src/engine/renderers/Indicator/scale/fastk_scale.ts +19 -19
  155. package/src/engine/renderers/Indicator/scale/indicator_scale.ts +204 -204
  156. package/src/engine/renderers/Indicator/scale/kst_scale.ts +19 -19
  157. package/src/engine/renderers/Indicator/scale/macd_scale.ts +22 -22
  158. package/src/engine/renderers/Indicator/scale/mom_scale.ts +19 -19
  159. package/src/engine/renderers/Indicator/scale/rsi_scale.ts +19 -19
  160. package/src/engine/renderers/Indicator/scale/stoch_scale.ts +19 -19
  161. package/src/engine/renderers/Indicator/scale/volume_scale.ts +26 -26
  162. package/src/engine/renderers/Indicator/scale/wmsr_scale.ts +19 -19
  163. package/src/engine/renderers/Indicator/stoch.ts +359 -359
  164. package/src/engine/renderers/Indicator/structure.ts +126 -126
  165. package/src/engine/renderers/Indicator/subPaneConfig.ts +265 -265
  166. package/src/engine/renderers/Indicator/supertrend.ts +115 -115
  167. package/src/engine/renderers/Indicator/tema.ts +136 -136
  168. package/src/engine/renderers/Indicator/trix.ts +158 -158
  169. package/src/engine/renderers/Indicator/vma.ts +124 -124
  170. package/src/engine/renderers/Indicator/volumeProfile.ts +125 -125
  171. package/src/engine/renderers/Indicator/vwap.ts +123 -123
  172. package/src/engine/renderers/Indicator/wma.ts +136 -136
  173. package/src/engine/renderers/Indicator/wmsr.ts +328 -328
  174. package/src/engine/renderers/Indicator/zones.ts +104 -104
  175. package/src/engine/renderers/__tests__/boll.renderer.test.ts +314 -314
  176. package/src/engine/renderers/__tests__/ene.renderer.test.ts +305 -305
  177. package/src/engine/renderers/__tests__/expma.renderer.test.ts +279 -279
  178. package/src/engine/renderers/__tests__/ma.renderer.test.ts +426 -426
  179. package/src/engine/renderers/__tests__/mainIndicatorLegend.renderer.test.ts +502 -502
  180. package/src/engine/renderers/__tests__/yAxis.renderer.test.ts +173 -173
  181. package/src/engine/renderers/candle.ts +459 -459
  182. package/src/engine/renderers/crosshair.ts +69 -69
  183. package/src/engine/renderers/customMarkers.ts +162 -162
  184. package/src/engine/renderers/extremaMarkers.ts +246 -246
  185. package/src/engine/renderers/gridLines.ts +90 -90
  186. package/src/engine/renderers/lastPrice.ts +97 -97
  187. package/src/engine/renderers/paneTitle.ts +136 -136
  188. package/src/engine/renderers/subVolume.ts +236 -236
  189. package/src/engine/renderers/timeAxis.ts +121 -121
  190. package/src/engine/renderers/webgl/candleSurface.ts +955 -955
  191. package/src/engine/renderers/webgl/sharedWebGLSurface.ts +146 -146
  192. package/src/engine/renderers/yAxis.ts +105 -105
  193. package/src/engine/scale/__tests__/logFormula.spec.ts +148 -148
  194. package/src/engine/scale/logFormula.ts +130 -130
  195. package/src/engine/scale/price.ts +39 -39
  196. package/src/engine/scale/priceScale.ts +264 -264
  197. package/src/engine/subPaneManager.ts +427 -427
  198. package/src/engine/theme/colors.ts +642 -642
  199. package/src/engine/theme/fonts.ts +20 -20
  200. package/src/engine/utils/klineConfig.ts +49 -49
  201. package/src/engine/utils/tickCount.ts +11 -11
  202. package/src/engine/utils/tickPosition.ts +214 -214
  203. package/src/engine/utils/zoom.ts +83 -83
  204. package/src/engine/viewport/viewport.ts +67 -67
  205. package/src/index.ts +3 -3
  206. package/src/plugin/ConfigManager.ts +93 -93
  207. package/src/plugin/EventBus.ts +77 -77
  208. package/src/plugin/HookSystem.ts +106 -106
  209. package/src/plugin/PluginHost.ts +243 -243
  210. package/src/plugin/PluginRegistry.ts +92 -92
  211. package/src/plugin/StateStore.ts +73 -73
  212. package/src/plugin/index.ts +19 -19
  213. package/src/plugin/rendererPluginManager.ts +368 -368
  214. package/src/plugin/stateKeys.ts +8 -8
  215. package/src/plugin/types.ts +526 -526
  216. package/src/reactivity/index.ts +2 -2
  217. package/src/reactivity/signal.ts +119 -119
  218. package/src/semantic/controller.ts +251 -251
  219. package/src/semantic/drawShape.ts +260 -260
  220. package/src/semantic/index.ts +28 -28
  221. package/src/semantic/schema.json +256 -256
  222. package/src/semantic/types.ts +251 -251
  223. package/src/semantic/validator.ts +349 -349
  224. package/src/types/kLine.ts +13 -13
  225. package/src/types/price.ts +56 -56
  226. package/src/types/volumePrice.ts +33 -33
  227. package/src/utils/dateFormat.ts +208 -208
  228. package/src/utils/kLineDraw/axis.ts +562 -562
  229. package/src/utils/priceToY.ts +34 -34
  230. package/src/utils/volumePrice.ts +202 -202
  231. package/src/version.ts +1 -1
@@ -1,20 +1,20 @@
1
- /**
2
- * 图表字体配置
3
- * 字体栈:Trebuchet MS (Windows系统字体) -> Roboto -> Ubuntu -> 通用无衬线字体
4
- * 通过 Google Fonts CDN 加载 Roboto 和 Ubuntu
5
- */
6
-
7
- /** 数字和文本的标准字体栈 */
8
- export const FONT_FAMILY = '"Trebuchet MS", Roboto, Ubuntu, sans-serif'
9
-
10
- /** 获取指定字号的字体字符串,用于 Canvas ctx.font */
11
- export function getFont(size: number, options?: { bold?: boolean }): string {
12
- const weight = options?.bold ? 'bold ' : ''
13
- return `${weight}${size}px ${FONT_FAMILY}`
14
- }
15
-
16
- export function setCanvasFont(ctx: CanvasRenderingContext2D, font: string): void {
17
- if (ctx.font !== font) {
18
- ctx.font = font
19
- }
20
- }
1
+ /**
2
+ * 图表字体配置
3
+ * 字体栈:Trebuchet MS (Windows系统字体) -> Roboto -> Ubuntu -> 通用无衬线字体
4
+ * 通过 Google Fonts CDN 加载 Roboto 和 Ubuntu
5
+ */
6
+
7
+ /** 数字和文本的标准字体栈 */
8
+ export const FONT_FAMILY = '"Trebuchet MS", Roboto, Ubuntu, sans-serif'
9
+
10
+ /** 获取指定字号的字体字符串,用于 Canvas ctx.font */
11
+ export function getFont(size: number, options?: { bold?: boolean }): string {
12
+ const weight = options?.bold ? 'bold ' : ''
13
+ return `${weight}${size}px ${FONT_FAMILY}`
14
+ }
15
+
16
+ export function setCanvasFont(ctx: CanvasRenderingContext2D, font: string): void {
17
+ if (ctx.font !== font) {
18
+ ctx.font = font
19
+ }
20
+ }
@@ -1,49 +1,49 @@
1
- /**
2
- * K 线物理像素配置工具函数
3
- * 用于统一渲染、交互、视口计算的坐标系统
4
- */
5
-
6
- /**
7
- * 计算奇数化后的 K 线宽度(物理像素),确保影线能完美居中显示
8
- * @param kWidth K 线宽度(逻辑像素)
9
- * @param dpr 设备像素比
10
- * @returns 奇数化后的物理像素宽度
11
- */
12
- export function calcKWidthPx(kWidth: number, dpr: number): number {
13
- let kWidthPx = Math.round(kWidth * dpr)
14
- if (kWidthPx % 2 === 0) {
15
- kWidthPx += 1
16
- }
17
- return Math.max(1, kWidthPx)
18
- }
19
-
20
- /**
21
- * 获取图表渲染使用的物理像素配置
22
- * @param kWidth K 线宽度(逻辑像素)
23
- * @param kGap K 线间隙(逻辑像素)
24
- * @param dpr 设备像素比
25
- * @returns 物理像素和逻辑像素的配置对象
26
- */
27
- export function getPhysicalKLineConfig(kWidth: number, kGap: number, dpr: number) {
28
- const kWidthPx = calcKWidthPx(kWidth, dpr)
29
- const kGapPx = Math.round(kGap * dpr)
30
- const unitPx = kWidthPx + kGapPx
31
- const startXPx = kGapPx
32
-
33
- // 转回逻辑像素(供需要逻辑像素的地方使用)
34
- const kWidthLogical = kWidthPx / dpr
35
- const kGapLogical = kGapPx / dpr
36
- const unitLogical = unitPx / dpr
37
- const startXLogical = startXPx / dpr
38
-
39
- return {
40
- kWidthPx,
41
- kGapPx,
42
- unitPx,
43
- startXPx,
44
- kWidthLogical,
45
- kGapLogical,
46
- unitLogical,
47
- startXLogical,
48
- }
49
- }
1
+ /**
2
+ * K 线物理像素配置工具函数
3
+ * 用于统一渲染、交互、视口计算的坐标系统
4
+ */
5
+
6
+ /**
7
+ * 计算奇数化后的 K 线宽度(物理像素),确保影线能完美居中显示
8
+ * @param kWidth K 线宽度(逻辑像素)
9
+ * @param dpr 设备像素比
10
+ * @returns 奇数化后的物理像素宽度
11
+ */
12
+ export function calcKWidthPx(kWidth: number, dpr: number): number {
13
+ let kWidthPx = Math.round(kWidth * dpr)
14
+ if (kWidthPx % 2 === 0) {
15
+ kWidthPx += 1
16
+ }
17
+ return Math.max(1, kWidthPx)
18
+ }
19
+
20
+ /**
21
+ * 获取图表渲染使用的物理像素配置
22
+ * @param kWidth K 线宽度(逻辑像素)
23
+ * @param kGap K 线间隙(逻辑像素)
24
+ * @param dpr 设备像素比
25
+ * @returns 物理像素和逻辑像素的配置对象
26
+ */
27
+ export function getPhysicalKLineConfig(kWidth: number, kGap: number, dpr: number) {
28
+ const kWidthPx = calcKWidthPx(kWidth, dpr)
29
+ const kGapPx = Math.round(kGap * dpr)
30
+ const unitPx = kWidthPx + kGapPx
31
+ const startXPx = kGapPx
32
+
33
+ // 转回逻辑像素(供需要逻辑像素的地方使用)
34
+ const kWidthLogical = kWidthPx / dpr
35
+ const kGapLogical = kGapPx / dpr
36
+ const unitLogical = unitPx / dpr
37
+ const startXLogical = startXPx / dpr
38
+
39
+ return {
40
+ kWidthPx,
41
+ kGapPx,
42
+ unitPx,
43
+ startXPx,
44
+ kWidthLogical,
45
+ kGapLogical,
46
+ unitLogical,
47
+ startXLogical,
48
+ }
49
+ }
@@ -1,11 +1,11 @@
1
- /**
2
- * 根据面板高度计算网格线/价格标签数量
3
- * 刻度间距固定约 60px
4
- * @param height 面板高度(逻辑像素)
5
- * @param isMain 是否为主图面板(暂未使用,保留参数兼容)
6
- * @returns tick 数量
7
- */
8
- export function calculateTickCount(height: number, isMain?: boolean): number {
9
- const tickSpacing = 60 // 固定间距
10
- return Math.max(3, Math.round(height / tickSpacing) + 1)
11
- }
1
+ /**
2
+ * 根据面板高度计算网格线/价格标签数量
3
+ * 刻度间距固定约 60px
4
+ * @param height 面板高度(逻辑像素)
5
+ * @param isMain 是否为主图面板(暂未使用,保留参数兼容)
6
+ * @returns tick 数量
7
+ */
8
+ export function calculateTickCount(height: number, isMain?: boolean): number {
9
+ const tickSpacing = 60 // 固定间距
10
+ return Math.max(3, Math.round(height / tickSpacing) + 1)
11
+ }
@@ -1,214 +1,214 @@
1
- import { calculateTickCount } from './tickCount'
2
- import { fromLog, logFormulaForPriceRange, toLog } from '../scale/logFormula'
3
-
4
- export interface TickPosition {
5
- index: number
6
- t: number
7
- y: number
8
- }
9
-
10
- export type ScaleType = 'linear' | 'log'
11
-
12
- export interface CalculateTickPositionsOptions {
13
- height: number
14
- paddingTop: number
15
- paddingBottom: number
16
- isMain?: boolean
17
- hideEdgeTicks?: boolean
18
- scaleType?: ScaleType
19
- }
20
-
21
- export function calculateTickPositions(
22
- options: CalculateTickPositionsOptions
23
- ): TickPosition[] {
24
- const { height, paddingTop, paddingBottom, isMain, hideEdgeTicks } = options
25
-
26
- const yStart = paddingTop
27
- const yEnd = Math.max(paddingTop, height - paddingBottom)
28
- const viewH = Math.max(0, yEnd - yStart)
29
-
30
- const ticks = calculateTickCount(height, isMain)
31
- const positions: TickPosition[] = []
32
-
33
- for (let i = 0; i < ticks; i++) {
34
- if (hideEdgeTicks && (i === 0 || i === ticks - 1)) continue
35
-
36
- const t = ticks <= 1 ? 0 : i / (ticks - 1)
37
- const y = yStart + t * viewH
38
-
39
- positions.push({ index: i, t, y })
40
- }
41
-
42
- return positions
43
- }
44
-
45
- export interface TickPositionWithValue extends TickPosition {
46
- value: number
47
- }
48
-
49
- export interface CalculateValueTickPositionsOptions extends CalculateTickPositionsOptions {
50
- valueMin: number
51
- valueMax: number
52
- logBase?: number
53
- }
54
-
55
- const LOG_EPSILON = 1e-10
56
- const LOG_ROUND_CANDIDATES = [1, 1.2, 1.5, 2, 2.5, 3, 4, 5, 6, 7.5, 8, 10]
57
-
58
- function roundLogTickValue(value: number): number {
59
- if (!Number.isFinite(value) || value <= 0) {
60
- return value
61
- }
62
-
63
- const exp = Math.floor(Math.log10(value))
64
- const magnitude = Math.pow(10, exp)
65
- const normalized = value / magnitude
66
-
67
- let best = LOG_ROUND_CANDIDATES[0]
68
- let minDiff = Infinity
69
- for (const candidate of LOG_ROUND_CANDIDATES) {
70
- const diff = Math.abs(candidate - normalized)
71
- if (diff < minDiff) {
72
- minDiff = diff
73
- best = candidate
74
- }
75
- }
76
-
77
- return best * magnitude
78
- }
79
-
80
- function generatePixelUniformLogTicks(options: {
81
- height: number
82
- paddingTop: number
83
- paddingBottom: number
84
- valueMin: number
85
- valueMax: number
86
- targetCount: number
87
- }): TickPositionWithValue[] {
88
- const { height, paddingTop, paddingBottom, valueMin, valueMax, targetCount } = options
89
-
90
- const effectiveMin = Math.max(valueMin, LOG_EPSILON)
91
- const effectiveMax = Math.max(valueMax, effectiveMin * 1.001)
92
- const yStart = paddingTop
93
- const yEnd = Math.max(paddingTop, height - paddingBottom)
94
- const viewH = Math.max(0, yEnd - yStart)
95
-
96
- if (viewH <= 0) {
97
- return []
98
- }
99
-
100
- const formula = logFormulaForPriceRange({ minPrice: effectiveMin, maxPrice: effectiveMax })
101
- const logMin = toLog(effectiveMin, formula)
102
- const logMax = toLog(effectiveMax, formula)
103
- const logRange = logMax - logMin
104
-
105
- if (!Number.isFinite(logRange) || Math.abs(logRange) < 1e-10) {
106
- return []
107
- }
108
-
109
- const total = Math.max(2, targetCount)
110
- const spacingPx = total <= 1 ? viewH : viewH / (total - 1)
111
- const minSpacingPx = spacingPx * 0.6
112
- const maxRoundShiftPx = spacingPx * 0.15
113
-
114
- const positions: TickPositionWithValue[] = []
115
- let prevY: number | null = null
116
-
117
- for (let i = 0; i < total; i++) {
118
- const idealT = total <= 1 ? 0 : i / (total - 1)
119
- const idealY = yStart + idealT * viewH
120
- const logical = logMax - idealT * logRange
121
- const rawValue = fromLog(logical, formula)
122
-
123
- let value = rawValue
124
- let t = idealT
125
- let y = idealY
126
-
127
- const roundedValue = roundLogTickValue(rawValue)
128
- if (Number.isFinite(roundedValue) && roundedValue > 0) {
129
- const roundedLogical = toLog(roundedValue, formula)
130
- const roundedT = (logMax - roundedLogical) / logRange
131
- const roundedY = yStart + roundedT * viewH
132
-
133
- if (Number.isFinite(roundedY) && Math.abs(roundedY - idealY) <= maxRoundShiftPx) {
134
- value = roundedValue
135
- t = roundedT
136
- y = roundedY
137
- }
138
- }
139
-
140
- if (y < yStart - 0.5 || y > yEnd + 0.5) {
141
- continue
142
- }
143
-
144
- if (prevY !== null && Math.abs(y - prevY) < minSpacingPx) {
145
- continue
146
- }
147
-
148
- positions.push({ index: positions.length, t, y, value })
149
- prevY = y
150
- }
151
-
152
- return positions
153
- }
154
-
155
- // calculateValueTickPositions 缓存
156
- let _cvtpCacheKey = ''
157
- let _cvtpCacheResult: TickPositionWithValue[] = []
158
-
159
- function buildCvtpCacheKey(options: CalculateValueTickPositionsOptions): string {
160
- return `${options.valueMin}:${options.valueMax}:${options.height}:${options.paddingTop}:${options.paddingBottom}:${options.isMain}:${options.scaleType ?? 'linear'}:${options.hideEdgeTicks ?? false}`
161
- }
162
-
163
- export function calculateValueTickPositions(
164
- options: CalculateValueTickPositionsOptions
165
- ): TickPositionWithValue[] {
166
- // 缓存命中:所有参数相同则直接返回
167
- const key = buildCvtpCacheKey(options)
168
- if (key === _cvtpCacheKey) return _cvtpCacheResult
169
-
170
- const { valueMin, valueMax, scaleType = 'linear' } = options
171
-
172
- if (scaleType === 'log') {
173
- if (valueMin <= 0) {
174
- // 递归调用走线性路径,线性路径会更新缓存
175
- return calculateValueTickPositions({ ...options, scaleType: 'linear' })
176
- }
177
-
178
- const effectiveMin = Math.max(valueMin, LOG_EPSILON)
179
- const effectiveMax = Math.max(valueMax, effectiveMin * 1.001)
180
- const { height, paddingTop, paddingBottom, isMain, hideEdgeTicks } = options
181
- const targetCount = Math.max(2, calculateTickCount(height, isMain))
182
-
183
- let positions = generatePixelUniformLogTicks({
184
- height,
185
- paddingTop,
186
- paddingBottom,
187
- valueMin: effectiveMin,
188
- valueMax: effectiveMax,
189
- targetCount,
190
- })
191
-
192
- if (hideEdgeTicks && positions.length > 2) {
193
- positions = positions.slice(1, -1)
194
- }
195
-
196
- const result = positions.map((position, index) => ({ ...position, index }))
197
- _cvtpCacheKey = key
198
- _cvtpCacheResult = result
199
- return result
200
- }
201
-
202
- const basePositions = calculateTickPositions(options)
203
- const totalTicks = calculateTickCount(options.height, options.isMain)
204
- const valueRange = valueMax - valueMin || 1
205
-
206
- const result = basePositions.map((pos) => {
207
- const step = valueRange / Math.max(1, totalTicks - 1)
208
- const value = valueMax - step * pos.index
209
- return { ...pos, value }
210
- })
211
- _cvtpCacheKey = key
212
- _cvtpCacheResult = result
213
- return result
214
- }
1
+ import { calculateTickCount } from './tickCount'
2
+ import { fromLog, logFormulaForPriceRange, toLog } from '../scale/logFormula'
3
+
4
+ export interface TickPosition {
5
+ index: number
6
+ t: number
7
+ y: number
8
+ }
9
+
10
+ export type ScaleType = 'linear' | 'log'
11
+
12
+ export interface CalculateTickPositionsOptions {
13
+ height: number
14
+ paddingTop: number
15
+ paddingBottom: number
16
+ isMain?: boolean
17
+ hideEdgeTicks?: boolean
18
+ scaleType?: ScaleType
19
+ }
20
+
21
+ export function calculateTickPositions(
22
+ options: CalculateTickPositionsOptions
23
+ ): TickPosition[] {
24
+ const { height, paddingTop, paddingBottom, isMain, hideEdgeTicks } = options
25
+
26
+ const yStart = paddingTop
27
+ const yEnd = Math.max(paddingTop, height - paddingBottom)
28
+ const viewH = Math.max(0, yEnd - yStart)
29
+
30
+ const ticks = calculateTickCount(height, isMain)
31
+ const positions: TickPosition[] = []
32
+
33
+ for (let i = 0; i < ticks; i++) {
34
+ if (hideEdgeTicks && (i === 0 || i === ticks - 1)) continue
35
+
36
+ const t = ticks <= 1 ? 0 : i / (ticks - 1)
37
+ const y = yStart + t * viewH
38
+
39
+ positions.push({ index: i, t, y })
40
+ }
41
+
42
+ return positions
43
+ }
44
+
45
+ export interface TickPositionWithValue extends TickPosition {
46
+ value: number
47
+ }
48
+
49
+ export interface CalculateValueTickPositionsOptions extends CalculateTickPositionsOptions {
50
+ valueMin: number
51
+ valueMax: number
52
+ logBase?: number
53
+ }
54
+
55
+ const LOG_EPSILON = 1e-10
56
+ const LOG_ROUND_CANDIDATES = [1, 1.2, 1.5, 2, 2.5, 3, 4, 5, 6, 7.5, 8, 10]
57
+
58
+ function roundLogTickValue(value: number): number {
59
+ if (!Number.isFinite(value) || value <= 0) {
60
+ return value
61
+ }
62
+
63
+ const exp = Math.floor(Math.log10(value))
64
+ const magnitude = Math.pow(10, exp)
65
+ const normalized = value / magnitude
66
+
67
+ let best = LOG_ROUND_CANDIDATES[0]
68
+ let minDiff = Infinity
69
+ for (const candidate of LOG_ROUND_CANDIDATES) {
70
+ const diff = Math.abs(candidate - normalized)
71
+ if (diff < minDiff) {
72
+ minDiff = diff
73
+ best = candidate
74
+ }
75
+ }
76
+
77
+ return best * magnitude
78
+ }
79
+
80
+ function generatePixelUniformLogTicks(options: {
81
+ height: number
82
+ paddingTop: number
83
+ paddingBottom: number
84
+ valueMin: number
85
+ valueMax: number
86
+ targetCount: number
87
+ }): TickPositionWithValue[] {
88
+ const { height, paddingTop, paddingBottom, valueMin, valueMax, targetCount } = options
89
+
90
+ const effectiveMin = Math.max(valueMin, LOG_EPSILON)
91
+ const effectiveMax = Math.max(valueMax, effectiveMin * 1.001)
92
+ const yStart = paddingTop
93
+ const yEnd = Math.max(paddingTop, height - paddingBottom)
94
+ const viewH = Math.max(0, yEnd - yStart)
95
+
96
+ if (viewH <= 0) {
97
+ return []
98
+ }
99
+
100
+ const formula = logFormulaForPriceRange({ minPrice: effectiveMin, maxPrice: effectiveMax })
101
+ const logMin = toLog(effectiveMin, formula)
102
+ const logMax = toLog(effectiveMax, formula)
103
+ const logRange = logMax - logMin
104
+
105
+ if (!Number.isFinite(logRange) || Math.abs(logRange) < 1e-10) {
106
+ return []
107
+ }
108
+
109
+ const total = Math.max(2, targetCount)
110
+ const spacingPx = total <= 1 ? viewH : viewH / (total - 1)
111
+ const minSpacingPx = spacingPx * 0.6
112
+ const maxRoundShiftPx = spacingPx * 0.15
113
+
114
+ const positions: TickPositionWithValue[] = []
115
+ let prevY: number | null = null
116
+
117
+ for (let i = 0; i < total; i++) {
118
+ const idealT = total <= 1 ? 0 : i / (total - 1)
119
+ const idealY = yStart + idealT * viewH
120
+ const logical = logMax - idealT * logRange
121
+ const rawValue = fromLog(logical, formula)
122
+
123
+ let value = rawValue
124
+ let t = idealT
125
+ let y = idealY
126
+
127
+ const roundedValue = roundLogTickValue(rawValue)
128
+ if (Number.isFinite(roundedValue) && roundedValue > 0) {
129
+ const roundedLogical = toLog(roundedValue, formula)
130
+ const roundedT = (logMax - roundedLogical) / logRange
131
+ const roundedY = yStart + roundedT * viewH
132
+
133
+ if (Number.isFinite(roundedY) && Math.abs(roundedY - idealY) <= maxRoundShiftPx) {
134
+ value = roundedValue
135
+ t = roundedT
136
+ y = roundedY
137
+ }
138
+ }
139
+
140
+ if (y < yStart - 0.5 || y > yEnd + 0.5) {
141
+ continue
142
+ }
143
+
144
+ if (prevY !== null && Math.abs(y - prevY) < minSpacingPx) {
145
+ continue
146
+ }
147
+
148
+ positions.push({ index: positions.length, t, y, value })
149
+ prevY = y
150
+ }
151
+
152
+ return positions
153
+ }
154
+
155
+ // calculateValueTickPositions 缓存
156
+ let _cvtpCacheKey = ''
157
+ let _cvtpCacheResult: TickPositionWithValue[] = []
158
+
159
+ function buildCvtpCacheKey(options: CalculateValueTickPositionsOptions): string {
160
+ return `${options.valueMin}:${options.valueMax}:${options.height}:${options.paddingTop}:${options.paddingBottom}:${options.isMain}:${options.scaleType ?? 'linear'}:${options.hideEdgeTicks ?? false}`
161
+ }
162
+
163
+ export function calculateValueTickPositions(
164
+ options: CalculateValueTickPositionsOptions
165
+ ): TickPositionWithValue[] {
166
+ // 缓存命中:所有参数相同则直接返回
167
+ const key = buildCvtpCacheKey(options)
168
+ if (key === _cvtpCacheKey) return _cvtpCacheResult
169
+
170
+ const { valueMin, valueMax, scaleType = 'linear' } = options
171
+
172
+ if (scaleType === 'log') {
173
+ if (valueMin <= 0) {
174
+ // 递归调用走线性路径,线性路径会更新缓存
175
+ return calculateValueTickPositions({ ...options, scaleType: 'linear' })
176
+ }
177
+
178
+ const effectiveMin = Math.max(valueMin, LOG_EPSILON)
179
+ const effectiveMax = Math.max(valueMax, effectiveMin * 1.001)
180
+ const { height, paddingTop, paddingBottom, isMain, hideEdgeTicks } = options
181
+ const targetCount = Math.max(2, calculateTickCount(height, isMain))
182
+
183
+ let positions = generatePixelUniformLogTicks({
184
+ height,
185
+ paddingTop,
186
+ paddingBottom,
187
+ valueMin: effectiveMin,
188
+ valueMax: effectiveMax,
189
+ targetCount,
190
+ })
191
+
192
+ if (hideEdgeTicks && positions.length > 2) {
193
+ positions = positions.slice(1, -1)
194
+ }
195
+
196
+ const result = positions.map((position, index) => ({ ...position, index }))
197
+ _cvtpCacheKey = key
198
+ _cvtpCacheResult = result
199
+ return result
200
+ }
201
+
202
+ const basePositions = calculateTickPositions(options)
203
+ const totalTicks = calculateTickCount(options.height, options.isMain)
204
+ const valueRange = valueMax - valueMin || 1
205
+
206
+ const result = basePositions.map((pos) => {
207
+ const step = valueRange / Math.max(1, totalTicks - 1)
208
+ const value = valueMax - step * pos.index
209
+ return { ...pos, value }
210
+ })
211
+ _cvtpCacheKey = key
212
+ _cvtpCacheResult = result
213
+ return result
214
+ }