@retikz/core 0.1.0-alpha.4 → 0.1.0-beta.1

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 (275) hide show
  1. package/dist/es/compile/compile.d.ts +23 -13
  2. package/dist/es/compile/compile.d.ts.map +1 -1
  3. package/dist/es/compile/compile.js +44 -25
  4. package/dist/es/compile/index.d.ts +1 -1
  5. package/dist/es/compile/index.d.ts.map +1 -1
  6. package/dist/es/compile/node.d.ts +40 -59
  7. package/dist/es/compile/node.d.ts.map +1 -1
  8. package/dist/es/compile/node.js +51 -43
  9. package/dist/es/compile/parseTarget.d.ts +3 -8
  10. package/dist/es/compile/parseTarget.d.ts.map +1 -1
  11. package/dist/es/compile/parseTarget.js +7 -19
  12. package/dist/es/compile/path/anchor.d.ts +19 -0
  13. package/dist/es/compile/path/anchor.d.ts.map +1 -0
  14. package/dist/es/compile/path/anchor.js +54 -0
  15. package/dist/es/compile/path/index.d.ts +24 -0
  16. package/dist/es/compile/path/index.d.ts.map +1 -0
  17. package/dist/es/compile/path/index.js +308 -0
  18. package/dist/es/compile/path/label.d.ts +18 -0
  19. package/dist/es/compile/path/label.d.ts.map +1 -0
  20. package/dist/es/compile/path/label.js +102 -0
  21. package/dist/es/compile/path/relative.d.ts +8 -0
  22. package/dist/es/compile/path/relative.d.ts.map +1 -0
  23. package/dist/es/compile/path/relative.js +48 -0
  24. package/dist/es/compile/path/shrink.d.ts +24 -0
  25. package/dist/es/compile/path/shrink.d.ts.map +1 -0
  26. package/dist/es/compile/path/shrink.js +136 -0
  27. package/dist/es/compile/path/split.d.ts +15 -0
  28. package/dist/es/compile/path/split.d.ts.map +1 -0
  29. package/dist/es/compile/path/split.js +46 -0
  30. package/dist/es/compile/position.d.ts +4 -8
  31. package/dist/es/compile/position.d.ts.map +1 -1
  32. package/dist/es/compile/position.js +10 -10
  33. package/dist/es/compile/text-metrics.d.ts +6 -5
  34. package/dist/es/compile/text-metrics.d.ts.map +1 -1
  35. package/dist/es/compile/text-metrics.js +11 -5
  36. package/dist/es/compile/view-box.d.ts +1 -4
  37. package/dist/es/compile/view-box.d.ts.map +1 -1
  38. package/dist/es/compile/view-box.js +1 -4
  39. package/dist/es/geometry/_transform.d.ts +21 -0
  40. package/dist/es/geometry/_transform.d.ts.map +1 -0
  41. package/dist/es/geometry/_transform.js +27 -0
  42. package/dist/es/geometry/arc.d.ts +3 -28
  43. package/dist/es/geometry/arc.d.ts.map +1 -1
  44. package/dist/es/geometry/arc.js +4 -31
  45. package/dist/es/geometry/bend.d.ts +2 -13
  46. package/dist/es/geometry/bend.d.ts.map +1 -1
  47. package/dist/es/geometry/bend.js +2 -13
  48. package/dist/es/geometry/circle.d.ts +6 -15
  49. package/dist/es/geometry/circle.d.ts.map +1 -1
  50. package/dist/es/geometry/circle.js +2 -20
  51. package/dist/es/geometry/diamond.d.ts +10 -26
  52. package/dist/es/geometry/diamond.d.ts.map +1 -1
  53. package/dist/es/geometry/diamond.js +5 -30
  54. package/dist/es/geometry/ellipse.d.ts +10 -17
  55. package/dist/es/geometry/ellipse.d.ts.map +1 -1
  56. package/dist/es/geometry/ellipse.js +5 -24
  57. package/dist/es/geometry/point.d.ts +8 -9
  58. package/dist/es/geometry/point.d.ts.map +1 -1
  59. package/dist/es/geometry/point.js +8 -9
  60. package/dist/es/geometry/polar.d.ts +12 -21
  61. package/dist/es/geometry/polar.d.ts.map +1 -1
  62. package/dist/es/geometry/polar.js +8 -19
  63. package/dist/es/geometry/rect.d.ts +8 -32
  64. package/dist/es/geometry/rect.d.ts.map +1 -1
  65. package/dist/es/geometry/rect.js +6 -49
  66. package/dist/es/geometry/segment.d.ts +15 -18
  67. package/dist/es/geometry/segment.d.ts.map +1 -1
  68. package/dist/es/geometry/segment.js +9 -16
  69. package/dist/es/index.d.ts +13 -13
  70. package/dist/es/index.d.ts.map +1 -1
  71. package/dist/es/index.js +7 -4
  72. package/dist/es/ir/coordinate.d.ts +18 -10
  73. package/dist/es/ir/coordinate.d.ts.map +1 -1
  74. package/dist/es/ir/coordinate.js +6 -11
  75. package/dist/es/ir/font.d.ts +21 -0
  76. package/dist/es/ir/font.d.ts.map +1 -0
  77. package/dist/es/ir/font.js +15 -0
  78. package/dist/es/ir/index.d.ts +2 -0
  79. package/dist/es/ir/index.d.ts.map +1 -1
  80. package/dist/es/ir/node.d.ts +51 -181
  81. package/dist/es/ir/node.d.ts.map +1 -1
  82. package/dist/es/ir/node.js +15 -70
  83. package/dist/es/ir/path/arrow.d.ts +210 -12
  84. package/dist/es/ir/path/arrow.d.ts.map +1 -1
  85. package/dist/es/ir/path/arrow.js +39 -12
  86. package/dist/es/ir/path/path.d.ts +477 -153
  87. package/dist/es/ir/path/path.d.ts.map +1 -1
  88. package/dist/es/ir/path/path.js +6 -6
  89. package/dist/es/ir/path/step.d.ts +395 -223
  90. package/dist/es/ir/path/step.d.ts.map +1 -1
  91. package/dist/es/ir/path/step.js +21 -22
  92. package/dist/es/ir/path/target.d.ts +25 -16
  93. package/dist/es/ir/path/target.d.ts.map +1 -1
  94. package/dist/es/ir/path/target.js +8 -6
  95. package/dist/es/ir/position/at-position.d.ts +4 -11
  96. package/dist/es/ir/position/at-position.d.ts.map +1 -1
  97. package/dist/es/ir/position/at-position.js +2 -9
  98. package/dist/es/ir/position/index.d.ts +1 -0
  99. package/dist/es/ir/position/index.d.ts.map +1 -1
  100. package/dist/es/ir/position/offset-position.d.ts +14 -0
  101. package/dist/es/ir/position/offset-position.d.ts.map +1 -0
  102. package/dist/es/ir/position/offset-position.js +14 -0
  103. package/dist/es/ir/position/polar-position.d.ts +1 -4
  104. package/dist/es/ir/position/polar-position.d.ts.map +1 -1
  105. package/dist/es/ir/position/polar-position.js +3 -6
  106. package/dist/es/ir/position/position.d.ts.map +1 -1
  107. package/dist/es/ir/position/position.js +1 -1
  108. package/dist/es/ir/scene.d.ts +1236 -422
  109. package/dist/es/ir/scene.d.ts.map +1 -1
  110. package/dist/es/ir/text.d.ts +96 -0
  111. package/dist/es/ir/text.d.ts.map +1 -0
  112. package/dist/es/ir/text.js +20 -0
  113. package/dist/es/parsers/parseTargetSugar.d.ts +4 -0
  114. package/dist/es/parsers/parseTargetSugar.d.ts.map +1 -1
  115. package/dist/es/parsers/parseTargetSugar.js +7 -19
  116. package/dist/es/parsers/parseWay.d.ts +26 -118
  117. package/dist/es/parsers/parseWay.d.ts.map +1 -1
  118. package/dist/es/parsers/parseWay.js +19 -61
  119. package/dist/es/primitive/ellipse.d.ts +4 -14
  120. package/dist/es/primitive/ellipse.d.ts.map +1 -1
  121. package/dist/es/primitive/group.d.ts +37 -3
  122. package/dist/es/primitive/group.d.ts.map +1 -1
  123. package/dist/es/primitive/path.d.ts +107 -7
  124. package/dist/es/primitive/path.d.ts.map +1 -1
  125. package/dist/es/primitive/rect.d.ts +2 -2
  126. package/dist/es/primitive/rect.d.ts.map +1 -1
  127. package/dist/es/primitive/scene.d.ts +2 -4
  128. package/dist/es/primitive/scene.d.ts.map +1 -1
  129. package/dist/es/primitive/text.d.ts +16 -32
  130. package/dist/es/primitive/text.d.ts.map +1 -1
  131. package/dist/es/primitive/view-box.d.ts +1 -1
  132. package/dist/es/primitive/view-box.d.ts.map +1 -1
  133. package/dist/es/types.d.ts +8 -0
  134. package/dist/es/types.d.ts.map +1 -1
  135. package/dist/lib/compile/compile.cjs +44 -25
  136. package/dist/lib/compile/compile.d.ts +23 -13
  137. package/dist/lib/compile/compile.d.ts.map +1 -1
  138. package/dist/lib/compile/index.d.ts +1 -1
  139. package/dist/lib/compile/index.d.ts.map +1 -1
  140. package/dist/lib/compile/node.cjs +51 -43
  141. package/dist/lib/compile/node.d.ts +40 -59
  142. package/dist/lib/compile/node.d.ts.map +1 -1
  143. package/dist/lib/compile/parseTarget.cjs +7 -19
  144. package/dist/lib/compile/parseTarget.d.ts +3 -8
  145. package/dist/lib/compile/parseTarget.d.ts.map +1 -1
  146. package/dist/lib/compile/path/anchor.cjs +58 -0
  147. package/dist/lib/compile/path/anchor.d.ts +19 -0
  148. package/dist/lib/compile/path/anchor.d.ts.map +1 -0
  149. package/dist/lib/compile/path/index.cjs +308 -0
  150. package/dist/lib/compile/path/index.d.ts +24 -0
  151. package/dist/lib/compile/path/index.d.ts.map +1 -0
  152. package/dist/lib/compile/path/label.cjs +103 -0
  153. package/dist/lib/compile/path/label.d.ts +18 -0
  154. package/dist/lib/compile/path/label.d.ts.map +1 -0
  155. package/dist/lib/compile/path/relative.cjs +48 -0
  156. package/dist/lib/compile/path/relative.d.ts +8 -0
  157. package/dist/lib/compile/path/relative.d.ts.map +1 -0
  158. package/dist/lib/compile/path/shrink.cjs +138 -0
  159. package/dist/lib/compile/path/shrink.d.ts +24 -0
  160. package/dist/lib/compile/path/shrink.d.ts.map +1 -0
  161. package/dist/lib/compile/path/split.cjs +46 -0
  162. package/dist/lib/compile/path/split.d.ts +15 -0
  163. package/dist/lib/compile/path/split.d.ts.map +1 -0
  164. package/dist/lib/compile/position.cjs +10 -10
  165. package/dist/lib/compile/position.d.ts +4 -8
  166. package/dist/lib/compile/position.d.ts.map +1 -1
  167. package/dist/lib/compile/text-metrics.cjs +11 -5
  168. package/dist/lib/compile/text-metrics.d.ts +6 -5
  169. package/dist/lib/compile/text-metrics.d.ts.map +1 -1
  170. package/dist/lib/compile/view-box.cjs +1 -4
  171. package/dist/lib/compile/view-box.d.ts +1 -4
  172. package/dist/lib/compile/view-box.d.ts.map +1 -1
  173. package/dist/lib/geometry/_transform.cjs +28 -0
  174. package/dist/lib/geometry/_transform.d.ts +21 -0
  175. package/dist/lib/geometry/_transform.d.ts.map +1 -0
  176. package/dist/lib/geometry/arc.cjs +3 -31
  177. package/dist/lib/geometry/arc.d.ts +3 -28
  178. package/dist/lib/geometry/arc.d.ts.map +1 -1
  179. package/dist/lib/geometry/bend.cjs +2 -13
  180. package/dist/lib/geometry/bend.d.ts +2 -13
  181. package/dist/lib/geometry/bend.d.ts.map +1 -1
  182. package/dist/lib/geometry/circle.cjs +6 -24
  183. package/dist/lib/geometry/circle.d.ts +6 -15
  184. package/dist/lib/geometry/circle.d.ts.map +1 -1
  185. package/dist/lib/geometry/diamond.cjs +9 -34
  186. package/dist/lib/geometry/diamond.d.ts +10 -26
  187. package/dist/lib/geometry/diamond.d.ts.map +1 -1
  188. package/dist/lib/geometry/ellipse.cjs +9 -28
  189. package/dist/lib/geometry/ellipse.d.ts +10 -17
  190. package/dist/lib/geometry/ellipse.d.ts.map +1 -1
  191. package/dist/lib/geometry/point.cjs +8 -9
  192. package/dist/lib/geometry/point.d.ts +8 -9
  193. package/dist/lib/geometry/point.d.ts.map +1 -1
  194. package/dist/lib/geometry/polar.cjs +8 -19
  195. package/dist/lib/geometry/polar.d.ts +12 -21
  196. package/dist/lib/geometry/polar.d.ts.map +1 -1
  197. package/dist/lib/geometry/rect.cjs +10 -53
  198. package/dist/lib/geometry/rect.d.ts +8 -32
  199. package/dist/lib/geometry/rect.d.ts.map +1 -1
  200. package/dist/lib/geometry/segment.cjs +9 -16
  201. package/dist/lib/geometry/segment.d.ts +15 -18
  202. package/dist/lib/geometry/segment.d.ts.map +1 -1
  203. package/dist/lib/index.cjs +14 -5
  204. package/dist/lib/index.d.ts +13 -13
  205. package/dist/lib/index.d.ts.map +1 -1
  206. package/dist/lib/ir/coordinate.cjs +6 -11
  207. package/dist/lib/ir/coordinate.d.ts +18 -10
  208. package/dist/lib/ir/coordinate.d.ts.map +1 -1
  209. package/dist/lib/ir/font.cjs +15 -0
  210. package/dist/lib/ir/font.d.ts +21 -0
  211. package/dist/lib/ir/font.d.ts.map +1 -0
  212. package/dist/lib/ir/index.d.ts +2 -0
  213. package/dist/lib/ir/index.d.ts.map +1 -1
  214. package/dist/lib/ir/node.cjs +16 -74
  215. package/dist/lib/ir/node.d.ts +51 -181
  216. package/dist/lib/ir/node.d.ts.map +1 -1
  217. package/dist/lib/ir/path/arrow.cjs +43 -11
  218. package/dist/lib/ir/path/arrow.d.ts +210 -12
  219. package/dist/lib/ir/path/arrow.d.ts.map +1 -1
  220. package/dist/lib/ir/path/path.cjs +5 -5
  221. package/dist/lib/ir/path/path.d.ts +477 -153
  222. package/dist/lib/ir/path/path.d.ts.map +1 -1
  223. package/dist/lib/ir/path/step.cjs +21 -22
  224. package/dist/lib/ir/path/step.d.ts +395 -223
  225. package/dist/lib/ir/path/step.d.ts.map +1 -1
  226. package/dist/lib/ir/path/target.cjs +9 -7
  227. package/dist/lib/ir/path/target.d.ts +25 -16
  228. package/dist/lib/ir/path/target.d.ts.map +1 -1
  229. package/dist/lib/ir/position/at-position.cjs +2 -9
  230. package/dist/lib/ir/position/at-position.d.ts +4 -11
  231. package/dist/lib/ir/position/at-position.d.ts.map +1 -1
  232. package/dist/lib/ir/position/index.d.ts +1 -0
  233. package/dist/lib/ir/position/index.d.ts.map +1 -1
  234. package/dist/lib/ir/position/offset-position.cjs +14 -0
  235. package/dist/lib/ir/position/offset-position.d.ts +14 -0
  236. package/dist/lib/ir/position/offset-position.d.ts.map +1 -0
  237. package/dist/lib/ir/position/polar-position.cjs +3 -6
  238. package/dist/lib/ir/position/polar-position.d.ts +1 -4
  239. package/dist/lib/ir/position/polar-position.d.ts.map +1 -1
  240. package/dist/lib/ir/position/position.cjs +1 -1
  241. package/dist/lib/ir/position/position.d.ts.map +1 -1
  242. package/dist/lib/ir/scene.d.ts +1236 -422
  243. package/dist/lib/ir/scene.d.ts.map +1 -1
  244. package/dist/lib/ir/text.cjs +21 -0
  245. package/dist/lib/ir/text.d.ts +96 -0
  246. package/dist/lib/ir/text.d.ts.map +1 -0
  247. package/dist/lib/parsers/parseTargetSugar.cjs +7 -19
  248. package/dist/lib/parsers/parseTargetSugar.d.ts +4 -0
  249. package/dist/lib/parsers/parseTargetSugar.d.ts.map +1 -1
  250. package/dist/lib/parsers/parseWay.cjs +19 -61
  251. package/dist/lib/parsers/parseWay.d.ts +26 -118
  252. package/dist/lib/parsers/parseWay.d.ts.map +1 -1
  253. package/dist/lib/primitive/ellipse.d.ts +4 -14
  254. package/dist/lib/primitive/ellipse.d.ts.map +1 -1
  255. package/dist/lib/primitive/group.d.ts +37 -3
  256. package/dist/lib/primitive/group.d.ts.map +1 -1
  257. package/dist/lib/primitive/path.d.ts +107 -7
  258. package/dist/lib/primitive/path.d.ts.map +1 -1
  259. package/dist/lib/primitive/rect.d.ts +2 -2
  260. package/dist/lib/primitive/rect.d.ts.map +1 -1
  261. package/dist/lib/primitive/scene.d.ts +2 -4
  262. package/dist/lib/primitive/scene.d.ts.map +1 -1
  263. package/dist/lib/primitive/text.d.ts +16 -32
  264. package/dist/lib/primitive/text.d.ts.map +1 -1
  265. package/dist/lib/primitive/view-box.d.ts +1 -1
  266. package/dist/lib/primitive/view-box.d.ts.map +1 -1
  267. package/dist/lib/types.d.ts +8 -0
  268. package/dist/lib/types.d.ts.map +1 -1
  269. package/package.json +13 -4
  270. package/dist/es/compile/path.d.ts +0 -30
  271. package/dist/es/compile/path.d.ts.map +0 -1
  272. package/dist/es/compile/path.js +0 -617
  273. package/dist/lib/compile/path.cjs +0 -617
  274. package/dist/lib/compile/path.d.ts +0 -30
  275. package/dist/lib/compile/path.d.ts.map +0 -1
@@ -0,0 +1,24 @@
1
+ import { IRArrowDetail } from '../../ir';
2
+ import { ArrowEndSpec, PathCommand } from '../../primitive';
3
+ /** IR path-level `arrow` + `arrowDetail` → PathPrim 起末视觉规格 */
4
+ export declare const arrowMarkers: (arrow: "none" | "->" | "<-" | "<->" | undefined, detail: IRArrowDetail | undefined) => {
5
+ arrowStart?: ArrowEndSpec;
6
+ arrowEnd?: ArrowEndSpec;
7
+ };
8
+ /**
9
+ * 端点级 shrink(strokeWidth 倍):line 末端朝起点缩这么多,让 marker apex 落回原 target
10
+ * @description 不分实心/空心:所有 shape 都让 line 端点接在箭头尾部、apex 顶端仍贴原 target。低 opacity 下不会再透出 line。viewBox=10,shrink = (apex.x - refX) × length × scale / 10(strokeWidth 倍)。
11
+ *
12
+ * 几何对齐(必须与 react/render/arrowMarkers.tsx 中 renderInner 的 refX 一致):
13
+ * - `normal` / `diamond` / `circle`:apex 在 viewBox x=10、back 外缘 x=0 → refX=0,shrink = length × scale
14
+ * - `stealth`:apex x=10、V tip x=3(line 嵌进 V 凹口)→ refX=3,shrink = 0.7 × length × scale
15
+ * - `open` / `openDiamond`:apex x=9、back stroke 外缘 x = 1 - lineWidth/2 → refX = 1 - lineWidth/2,shrink = (8 + lineWidth/2) × length × scale / 10
16
+ * - `openCircle`:apex 外缘右 x ≈ 10、back 外缘左 x = 0.75 - lineWidth/2 → refX = 0.75 - lineWidth/2,shrink ≈ length × scale
17
+ */
18
+ export declare const computeShrink: (spec: ArrowEndSpec) => number;
19
+ /**
20
+ * 按 shape + spec(length / scale / lineWidth)把首/末段端点向内缩短
21
+ * @description 让 line 端点接在 hollow arrow 尾部外缘、不贯穿 back outline;shrink=0 的实心 shape 跳过。in-place 改写 commands 数组
22
+ */
23
+ export declare const applyArrowShrinks: (commands: Array<PathCommand>, shrinkStart: number, shrinkEnd: number, strokeWidth: number, round: (n: number) => number) => void;
24
+ //# sourceMappingURL=shrink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shrink.d.ts","sourceRoot":"","sources":["../../../../src/compile/path/shrink.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAgC,MAAM,UAAU,CAAC;AAC5E,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAkCjE,8DAA8D;AAC9D,eAAO,MAAM,YAAY,GACvB,OAAO,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,SAAS,EAC/C,QAAQ,aAAa,GAAG,SAAS,KAChC;IAAE,UAAU,CAAC,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,EAAE,YAAY,CAAA;CAatD,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,YAAY,KAAG,MAWlD,CAAC;AAiDF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAC5B,UAAU,KAAK,CAAC,WAAW,CAAC,EAC5B,aAAa,MAAM,EACnB,WAAW,MAAM,EACjB,aAAa,MAAM,EACnB,OAAO,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,KAC3B,IA4CF,CAAC"}
@@ -0,0 +1,136 @@
1
+ import { HOLLOW_ARROW_SHAPES } from "../../ir/path/arrow.js";
2
+ import { shiftToward } from "./anchor.js";
3
+ //#region src/compile/path/shrink.ts
4
+ /**
5
+ * 端点级 spec:顶层默认 ⊕ end-side override(逐字段 merge)
6
+ * @description 缺省字段继承顶层(不是"完全替换");空心 shape 上 fill 字段被丢(silent no-op)
7
+ */
8
+ var resolveArrowEndSpec = (topLevel, endSide) => {
9
+ const baseShape = endSide?.shape ?? topLevel.shape ?? "normal";
10
+ const out = { shape: baseShape };
11
+ const scale = endSide?.scale ?? topLevel.scale;
12
+ if (scale !== void 0) out.scale = scale;
13
+ const length = endSide?.length ?? topLevel.length;
14
+ if (length !== void 0) out.length = length;
15
+ const width = endSide?.width ?? topLevel.width;
16
+ if (width !== void 0) out.width = width;
17
+ const color = endSide?.color ?? topLevel.color;
18
+ if (color !== void 0) out.color = color;
19
+ const opacity = endSide?.opacity ?? topLevel.opacity;
20
+ if (opacity !== void 0) out.opacity = opacity;
21
+ const lineWidth = endSide?.lineWidth ?? topLevel.lineWidth;
22
+ if (lineWidth !== void 0) out.lineWidth = lineWidth;
23
+ if (!HOLLOW_ARROW_SHAPES.has(baseShape)) {
24
+ const fill = endSide?.fill ?? topLevel.fill;
25
+ if (fill !== void 0) out.fill = fill;
26
+ }
27
+ return out;
28
+ };
29
+ /** IR path-level `arrow` + `arrowDetail` → PathPrim 起末视觉规格 */
30
+ var arrowMarkers = (arrow, detail) => {
31
+ if (!arrow || arrow === "none") return {};
32
+ const top = detail ?? {};
33
+ const startSpec = resolveArrowEndSpec(top, top.start);
34
+ const endSpec = resolveArrowEndSpec(top, top.end);
35
+ switch (arrow) {
36
+ case "->": return { arrowEnd: endSpec };
37
+ case "<-": return { arrowStart: startSpec };
38
+ case "<->": return {
39
+ arrowStart: startSpec,
40
+ arrowEnd: endSpec
41
+ };
42
+ }
43
+ };
44
+ /**
45
+ * 端点级 shrink(strokeWidth 倍):line 末端朝起点缩这么多,让 marker apex 落回原 target
46
+ * @description 不分实心/空心:所有 shape 都让 line 端点接在箭头尾部、apex 顶端仍贴原 target。低 opacity 下不会再透出 line。viewBox=10,shrink = (apex.x - refX) × length × scale / 10(strokeWidth 倍)。
47
+ *
48
+ * 几何对齐(必须与 react/render/arrowMarkers.tsx 中 renderInner 的 refX 一致):
49
+ * - `normal` / `diamond` / `circle`:apex 在 viewBox x=10、back 外缘 x=0 → refX=0,shrink = length × scale
50
+ * - `stealth`:apex x=10、V tip x=3(line 嵌进 V 凹口)→ refX=3,shrink = 0.7 × length × scale
51
+ * - `open` / `openDiamond`:apex x=9、back stroke 外缘 x = 1 - lineWidth/2 → refX = 1 - lineWidth/2,shrink = (8 + lineWidth/2) × length × scale / 10
52
+ * - `openCircle`:apex 外缘右 x ≈ 10、back 外缘左 x = 0.75 - lineWidth/2 → refX = 0.75 - lineWidth/2,shrink ≈ length × scale
53
+ */
54
+ var computeShrink = (spec) => {
55
+ const length = (spec.length ?? 6) * (spec.scale ?? 1);
56
+ if (HOLLOW_ARROW_SHAPES.has(spec.shape)) {
57
+ if (spec.shape === "openCircle") return length;
58
+ return (8 + (spec.lineWidth ?? 1.5) / 2) * length / 10;
59
+ }
60
+ if (spec.shape === "stealth") return 7 * length / 10;
61
+ return length;
62
+ };
63
+ /** 取一个 PathCommand 末端 endpoint(move/line/quad/cubic → to;arc/ellipseArc → polar(end);close 无端点) */
64
+ var endpointOf = (cmd) => {
65
+ switch (cmd.kind) {
66
+ case "move":
67
+ case "line":
68
+ case "quad":
69
+ case "cubic": return [cmd.to[0], cmd.to[1]];
70
+ case "arc": {
71
+ const rad = cmd.endAngle * Math.PI / 180;
72
+ return [cmd.center[0] + Math.cos(rad) * cmd.radius, cmd.center[1] + Math.sin(rad) * cmd.radius];
73
+ }
74
+ case "ellipseArc": {
75
+ const rad = cmd.endAngle * Math.PI / 180;
76
+ return [cmd.center[0] + Math.cos(rad) * cmd.radiusX, cmd.center[1] + Math.sin(rad) * cmd.radiusY];
77
+ }
78
+ case "close": return null;
79
+ }
80
+ };
81
+ /** 改写一个 PathCommand 的 endpoint(用于 shrink) */
82
+ var setEndpoint = (commands, idx, newPt, round) => {
83
+ const cmd = commands[idx];
84
+ if (cmd.kind === "close") return;
85
+ const rp = [round(newPt[0]), round(newPt[1])];
86
+ if (cmd.kind === "move" || cmd.kind === "line") commands[idx] = {
87
+ ...cmd,
88
+ to: rp
89
+ };
90
+ else if (cmd.kind === "quad") commands[idx] = {
91
+ ...cmd,
92
+ to: rp
93
+ };
94
+ else if (cmd.kind === "cubic") commands[idx] = {
95
+ ...cmd,
96
+ to: rp
97
+ };
98
+ };
99
+ /**
100
+ * 按 shape + spec(length / scale / lineWidth)把首/末段端点向内缩短
101
+ * @description 让 line 端点接在 hollow arrow 尾部外缘、不贯穿 back outline;shrink=0 的实心 shape 跳过。in-place 改写 commands 数组
102
+ */
103
+ var applyArrowShrinks = (commands, shrinkStart, shrinkEnd, strokeWidth, round) => {
104
+ if (shrinkStart > 0) {
105
+ const firstIdx = commands.findIndex((o) => o.kind === "move");
106
+ if (firstIdx >= 0) {
107
+ const cur = commands[firstIdx];
108
+ const nextIdx = commands.findIndex((o, idx) => idx > firstIdx && o.kind !== "close");
109
+ if (cur.kind === "move" && nextIdx >= 0) {
110
+ const nextPt = endpointOf(commands[nextIdx]);
111
+ if (nextPt) setEndpoint(commands, firstIdx, shiftToward([cur.to[0], cur.to[1]], nextPt, shrinkStart * strokeWidth), round);
112
+ }
113
+ }
114
+ }
115
+ if (shrinkEnd > 0) {
116
+ let lastIdx = -1;
117
+ for (let i = commands.length - 1; i >= 0; i--) if (commands[i].kind !== "close") {
118
+ lastIdx = i;
119
+ break;
120
+ }
121
+ if (lastIdx > 0) {
122
+ let prevIdx = lastIdx - 1;
123
+ while (prevIdx >= 0 && commands[prevIdx].kind === "close") prevIdx--;
124
+ if (prevIdx >= 0) {
125
+ const curPt = endpointOf(commands[lastIdx]);
126
+ const prevPt = endpointOf(commands[prevIdx]);
127
+ if (curPt && prevPt) {
128
+ const shifted = shiftToward(curPt, prevPt, shrinkEnd * strokeWidth);
129
+ setEndpoint(commands, lastIdx, shifted, round);
130
+ }
131
+ }
132
+ }
133
+ }
134
+ };
135
+ //#endregion
136
+ export { applyArrowShrinks, arrowMarkers, computeShrink };
@@ -0,0 +1,15 @@
1
+ import { ArrowEndSpec, PathCommand, PathPrim, ScenePrimitive } from '../../primitive';
2
+ /** baseProps:除 commands/markers 外 PathPrim 公共属性集合(多 sub-path 复用) */
3
+ export type PathBaseProps = Omit<PathPrim, 'type' | 'commands' | 'arrowStart' | 'arrowEnd'>;
4
+ /**
5
+ * 多 sub-path + 有箭头:按 sub-path split 成多个 PathPrim,仅首段挂 arrowStart / 末段挂 arrowEnd,包进 GroupPrim
6
+ * @description SVG marker 按每个 sub-path 单独贴会在中间节点视觉错乱,故仅首末段挂;单 sub-path 或无箭头直接产一个 PathPrim
7
+ */
8
+ export declare const splitSubPathsForMarkers: (commands: Array<PathCommand>, baseProps: PathBaseProps, markers: {
9
+ arrowStart?: ArrowEndSpec;
10
+ arrowEnd?: ArrowEndSpec;
11
+ }) => {
12
+ primitive: ScenePrimitive;
13
+ isGrouped: boolean;
14
+ };
15
+ //# sourceMappingURL=split.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"split.d.ts","sourceRoot":"","sources":["../../../../src/compile/path/split.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAE3F,oEAAoE;AACpE,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,YAAY,GAAG,UAAU,CAAC,CAAC;AAE5F;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GAClC,UAAU,KAAK,CAAC,WAAW,CAAC,EAC5B,WAAW,aAAa,EACxB,SAAS;IAAE,UAAU,CAAC,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,EAAE,YAAY,CAAA;CAAE,KAC9D;IAAE,SAAS,EAAE,cAAc,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CA4CjD,CAAC"}
@@ -0,0 +1,46 @@
1
+ //#region src/compile/path/split.ts
2
+ /**
3
+ * 多 sub-path + 有箭头:按 sub-path split 成多个 PathPrim,仅首段挂 arrowStart / 末段挂 arrowEnd,包进 GroupPrim
4
+ * @description SVG marker 按每个 sub-path 单独贴会在中间节点视觉错乱,故仅首末段挂;单 sub-path 或无箭头直接产一个 PathPrim
5
+ */
6
+ var splitSubPathsForMarkers = (commands, baseProps, markers) => {
7
+ const hasArrows = !!markers.arrowStart || !!markers.arrowEnd;
8
+ const subPathStarts = [];
9
+ commands.forEach((cmd, idx) => {
10
+ if (cmd.kind === "move") subPathStarts.push(idx);
11
+ });
12
+ if (!hasArrows || subPathStarts.length <= 1) return {
13
+ primitive: {
14
+ type: "path",
15
+ commands,
16
+ ...baseProps,
17
+ ...markers
18
+ },
19
+ isGrouped: false
20
+ };
21
+ const subPathSlices = [];
22
+ for (let s = 0; s < subPathStarts.length; s++) {
23
+ const start = subPathStarts[s];
24
+ const end = s + 1 < subPathStarts.length ? subPathStarts[s + 1] : commands.length;
25
+ subPathSlices.push(commands.slice(start, end));
26
+ }
27
+ return {
28
+ primitive: {
29
+ type: "group",
30
+ children: subPathSlices.map((sub, i) => {
31
+ const isFirst = i === 0;
32
+ const isLast = i === subPathSlices.length - 1;
33
+ return {
34
+ type: "path",
35
+ commands: sub,
36
+ ...baseProps,
37
+ ...isFirst && markers.arrowStart ? { arrowStart: markers.arrowStart } : {},
38
+ ...isLast && markers.arrowEnd ? { arrowEnd: markers.arrowEnd } : {}
39
+ };
40
+ })
41
+ },
42
+ isGrouped: true
43
+ };
44
+ };
45
+ //#endregion
46
+ export { splitSubPathsForMarkers };
@@ -1,12 +1,8 @@
1
- import { IRAtPosition, IRPosition, PolarPosition } from '../ir';
1
+ import { IRAtPosition, IROffsetPosition, IRPosition, PolarPosition } from '../ir';
2
2
  import { NodeLayout } from './node';
3
3
  /**
4
- * IR 里出现的位置形态(笛卡尔 / 极坐标 / 相对定位 / 节点 id)解析为笛卡尔位置。
5
- * - 极坐标的 origin 可递归 / 可引用节点 id
6
- * - 相对定位的 of 必须引用已定义的节点 / coordinate(前向引用要求被引节点先出现)
7
- * - 解析失败返回 null(如引用了未定义节点)
8
- *
9
- * `nodeDistance` 是 Tikz 容器 prop 注入的相对定位默认距离;AtPosition 自带 distance 时优先用自带值。
4
+ * IR 各种位置形态(笛卡尔/极坐标/相对定位/偏移定位/节点 id)→ 笛卡尔位置
5
+ * @description 极坐标 origin / 偏移定位 of 均可递归引用节点 id 或字面坐标;relative `AtPosition` of 必须引用已定义节点/coordinate;解析失败返回 null。nodeDistance 为容器 prop 注入默认距离,AtPosition 自带 distance 优先
10
6
  */
11
- export declare const resolvePosition: (pos: IRPosition | PolarPosition | IRAtPosition | string, nodeMap: Map<string, NodeLayout>, nodeDistance?: number) => IRPosition | null;
7
+ export declare const resolvePosition: (pos: IRPosition | PolarPosition | IRAtPosition | IROffsetPosition | string, nodeMap: Map<string, NodeLayout>, nodeDistance?: number) => IRPosition | null;
12
8
  //# sourceMappingURL=position.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"position.d.ts","sourceRoot":"","sources":["../../../src/compile/position.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAClF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAqBzC;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAC1B,KAAK,UAAU,GAAG,aAAa,GAAG,YAAY,GAAG,MAAM,EACvD,SAAS,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EAChC,eAAc,MAA8B,KAC3C,UAAU,GAAG,IA4Bf,CAAC"}
1
+ {"version":3,"file":"position.d.ts","sourceRoot":"","sources":["../../../src/compile/position.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACpG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAoBzC;;;GAGG;AACH,eAAO,MAAM,eAAe,GAC1B,KAAK,UAAU,GAAG,aAAa,GAAG,YAAY,GAAG,gBAAgB,GAAG,MAAM,EAC1E,SAAS,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EAChC,eAAc,MAA8B,KAC3C,UAAU,GAAG,IAkCf,CAAC"}
@@ -1,10 +1,9 @@
1
1
  //#region src/compile/position.ts
2
- /** 默认相对定位距离(user units)——CompileOptions.nodeDistance 未提供时使用 */
2
+ /** 默认相对定位距离(CompileOptions.nodeDistance 未配时使用) */
3
3
  var DEFAULT_NODE_DISTANCE = 1;
4
4
  /**
5
- * 8 方向到屏幕坐标系(y 向下)单位向量的映射。
6
- * 视觉语义:above 在视觉上方(y 减小),below 在视觉下方(y 增大)。
7
- * 4 对角分量为 1/√2,保证斜向距离与水平 / 垂直距离等长(对角点位于半径 = distance 的圆周上)。
5
+ * 8 方向 → 屏幕坐标系(y 向下)单位向量
6
+ * @description above=视觉上方 (y 减小);4 对角分量 1/√2 保证斜向与水平/垂直距离等长(对角点落在半径=distance 圆周上)
8
7
  */
9
8
  var DIRECTION_VECTOR = {
10
9
  above: [0, -1],
@@ -17,12 +16,8 @@ var DIRECTION_VECTOR = {
17
16
  "below-right": [Math.SQRT1_2, Math.SQRT1_2]
18
17
  };
19
18
  /**
20
- * IR 里出现的位置形态(笛卡尔 / 极坐标 / 相对定位 / 节点 id)解析为笛卡尔位置。
21
- * - 极坐标的 origin 可递归 / 可引用节点 id
22
- * - 相对定位的 of 必须引用已定义的节点 / coordinate(前向引用要求被引节点先出现)
23
- * - 解析失败返回 null(如引用了未定义节点)
24
- *
25
- * `nodeDistance` 是 Tikz 容器 prop 注入的相对定位默认距离;AtPosition 自带 distance 时优先用自带值。
19
+ * IR 各种位置形态(笛卡尔/极坐标/相对定位/偏移定位/节点 id)→ 笛卡尔位置
20
+ * @description 极坐标 origin / 偏移定位 of 均可递归引用节点 id 或字面坐标;relative `AtPosition` of 必须引用已定义节点/coordinate;解析失败返回 null。nodeDistance 为容器 prop 注入默认距离,AtPosition 自带 distance 优先
26
21
  */
27
22
  var resolvePosition = (pos, nodeMap, nodeDistance = DEFAULT_NODE_DISTANCE) => {
28
23
  if (typeof pos === "string") {
@@ -37,6 +32,11 @@ var resolvePosition = (pos, nodeMap, nodeDistance = DEFAULT_NODE_DISTANCE) => {
37
32
  const [dx, dy] = DIRECTION_VECTOR[pos.direction];
38
33
  return [ref.rect.x + dx * distance, ref.rect.y + dy * distance];
39
34
  }
35
+ if ("offset" in pos) {
36
+ const base = resolvePosition(pos.of, nodeMap, nodeDistance);
37
+ if (!base) return null;
38
+ return [base[0] + pos.offset[0], base[1] + pos.offset[1]];
39
+ }
40
40
  let origin;
41
41
  if (!pos.origin) origin = [0, 0];
42
42
  else {
@@ -21,12 +21,13 @@ export type TextMetrics = {
21
21
  descent?: number;
22
22
  };
23
23
  /**
24
- * 文字度量函数接口(编译期由 adapter 注入)。
25
- * - @retikz/react:用 canvas measureText
26
- * - @retikz/ssr:用 opentype.js / fontkit
27
- * - @retikz/canvas:用 ctx.measureText
24
+ * 文字度量函数接口(编译期由 adapter 注入)
25
+ * @description @retikz/react: canvas measureText;@retikz/ssr: opentype.js/fontkit;@retikz/canvas: ctx.measureText
28
26
  */
29
27
  export type TextMeasurer = (text: string, font: FontSpec) => TextMetrics;
30
- /** 默认 fallback 度量:基于平均字宽估算,不准但保证可运行 */
28
+ /**
29
+ * 默认 fallback 度量:基于平均字宽估算,不准但保证可运行
30
+ * @description size=0 → 退化返回 (0, 0)(与 text='' 一致);负 size 或 NaN size → throw(非法输入早 fail,避免 NaN 噪音传播到 Scene)
31
+ */
31
32
  export declare const fallbackMeasurer: TextMeasurer;
32
33
  //# sourceMappingURL=text-metrics.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"text-metrics.d.ts","sourceRoot":"","sources":["../../../src/compile/text-metrics.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,MAAM,MAAM,QAAQ,GAAG;IACrB,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,mCAAmC;IACnC,KAAK,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;CACzC,CAAC;AAEF,uCAAuC;AACvC,MAAM,MAAM,WAAW,GAAG;IACxB,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,KAAK,WAAW,CAAC;AAEzE,uCAAuC;AACvC,eAAO,MAAM,gBAAgB,EAAE,YAG7B,CAAC"}
1
+ {"version":3,"file":"text-metrics.d.ts","sourceRoot":"","sources":["../../../src/compile/text-metrics.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,MAAM,MAAM,QAAQ,GAAG;IACrB,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,mCAAmC;IACnC,KAAK,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;CACzC,CAAC;AAEF,uCAAuC;AACvC,MAAM,MAAM,WAAW,GAAG;IACxB,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,KAAK,WAAW,CAAC;AAEzE;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,YAU9B,CAAC"}
@@ -1,8 +1,14 @@
1
1
  //#region src/compile/text-metrics.ts
2
- /** 默认 fallback 度量:基于平均字宽估算,不准但保证可运行 */
3
- var fallbackMeasurer = (text, font) => ({
4
- width: text.length * font.size * .55,
5
- height: font.size * 1.2
6
- });
2
+ /**
3
+ * 默认 fallback 度量:基于平均字宽估算,不准但保证可运行
4
+ * @description size=0 → 退化返回 (0, 0)(与 text='' 一致);负 size NaN size → throw(非法输入早 fail,避免 NaN 噪音传播到 Scene)
5
+ */
6
+ var fallbackMeasurer = (text, font) => {
7
+ if (Number.isNaN(font.size) || font.size < 0) throw new Error(`fallbackMeasurer: invalid font.size '${font.size}'; must be a non-negative finite number`);
8
+ return {
9
+ width: text.length * font.size * .55,
10
+ height: font.size * 1.2
11
+ };
12
+ };
7
13
  //#endregion
8
14
  export { fallbackMeasurer };
@@ -1,8 +1,5 @@
1
1
  import { IRPosition } from '../ir';
2
2
  import { ViewBox } from '../primitive';
3
- /**
4
- * 由所有"贡献给 bbox"的点算出 viewBox(含四周 padding)。
5
- * points 为空时返回固定的 100×100 兜底框。
6
- */
3
+ /** 由所有 bbox 候选点算出 viewBox(含 padding);points 为空时返回 100×100 兜底 */
7
4
  export declare const computeViewBox: (points: Array<IRPosition>, padding: number, round: (n: number) => number) => ViewBox;
8
5
  //# sourceMappingURL=view-box.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"view-box.d.ts","sourceRoot":"","sources":["../../../src/compile/view-box.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,cAAc,GACzB,QAAQ,KAAK,CAAC,UAAU,CAAC,EACzB,SAAS,MAAM,EACf,OAAO,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,KAC3B,OAkBF,CAAC"}
1
+ {"version":3,"file":"view-box.d.ts","sourceRoot":"","sources":["../../../src/compile/view-box.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,gEAAgE;AAChE,eAAO,MAAM,cAAc,GACzB,QAAQ,KAAK,CAAC,UAAU,CAAC,EACzB,SAAS,MAAM,EACf,OAAO,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,KAC3B,OAkBF,CAAC"}
@@ -1,8 +1,5 @@
1
1
  //#region src/compile/view-box.ts
2
- /**
3
- * 由所有"贡献给 bbox"的点算出 viewBox(含四周 padding)。
4
- * points 为空时返回固定的 100×100 兜底框。
5
- */
2
+ /** 由所有 bbox 候选点算出 viewBox(含 padding);points 为空时返回 100×100 兜底 */
6
3
  var computeViewBox = (points, padding, round) => {
7
4
  if (points.length === 0) return {
8
5
  x: 0,
@@ -0,0 +1,21 @@
1
+ import { Position } from './point';
2
+ /** 任何"中心 + 可选旋转"形状的几何契约(rect / circle / ellipse / diamond 共用) */
3
+ export type CenteredShape = {
4
+ /** 中心横坐标 */
5
+ x: number;
6
+ /** 中心纵坐标 */
7
+ y: number;
8
+ /** 绕中心旋转弧度(可选,0 / 缺省 = 不旋转) */
9
+ rotate?: number;
10
+ };
11
+ /**
12
+ * 本地坐标(以中心为原点)→ 世界坐标
13
+ * @description rotate=0 / 缺省时退化为平移;非零时按右手系绕中心旋转后再平移
14
+ */
15
+ export declare const localToWorld: (s: CenteredShape, local: Position) => Position;
16
+ /**
17
+ * 世界坐标 → 本地坐标(`localToWorld` 逆变换)
18
+ * @description 先反平移到中心、再反旋转回本地基;旋转矩阵转置即逆
19
+ */
20
+ export declare const worldToLocal: (s: CenteredShape, world: Position) => Position;
21
+ //# sourceMappingURL=_transform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_transform.d.ts","sourceRoot":"","sources":["../../../src/geometry/_transform.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,iEAAiE;AACjE,MAAM,MAAM,aAAa,GAAG;IAC1B,YAAY;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,YAAY;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,+BAA+B;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,GAAG,aAAa,EAAE,OAAO,QAAQ,KAAG,QAShE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,GAAG,aAAa,EAAE,OAAO,QAAQ,KAAG,QAQhE,CAAC"}
@@ -0,0 +1,27 @@
1
+ //#region src/geometry/_transform.ts
2
+ /**
3
+ * 本地坐标(以中心为原点)→ 世界坐标
4
+ * @description rotate=0 / 缺省时退化为平移;非零时按右手系绕中心旋转后再平移
5
+ */
6
+ var localToWorld = (s, local) => {
7
+ const angle = s.rotate ?? 0;
8
+ if (angle === 0) return [s.x + local[0], s.y + local[1]];
9
+ const cos = Math.cos(angle);
10
+ const sin = Math.sin(angle);
11
+ return [s.x + local[0] * cos - local[1] * sin, s.y + local[0] * sin + local[1] * cos];
12
+ };
13
+ /**
14
+ * 世界坐标 → 本地坐标(`localToWorld` 逆变换)
15
+ * @description 先反平移到中心、再反旋转回本地基;旋转矩阵转置即逆
16
+ */
17
+ var worldToLocal = (s, world) => {
18
+ const tx = world[0] - s.x;
19
+ const ty = world[1] - s.y;
20
+ const angle = s.rotate ?? 0;
21
+ if (angle === 0) return [tx, ty];
22
+ const cos = Math.cos(angle);
23
+ const sin = Math.sin(angle);
24
+ return [tx * cos + ty * sin, -tx * sin + ty * cos];
25
+ };
26
+ //#endregion
27
+ export { localToWorld, worldToLocal };
@@ -1,34 +1,9 @@
1
1
  import { Position } from './point';
2
- /**
3
- * 给定圆心、半径、角度(度,与 polar.toPosition 同约定),返回圆周上对应点。
4
- */
2
+ /** 圆心、半径、角度(度,与 polar.toPosition 同约定)→ 圆周上对应点 */
5
3
  export declare const arcEndPoint: (center: Position, radius: number, angleDeg: number) => Position;
6
4
  /**
7
- * 计算 SVG `<path>` A 命令需要的 large-arc-flag 与 sweep-flag。
8
- *
9
- * - `largeArc`:弧跨度 `|endAngle - startAngle|` 严格大于 180° 时为 1。
10
- * - `sweep`:`endAngle >= startAngle` 时为 1,否则为 0;与"角度增加方向 = 在
11
- * SVG 屏幕上视觉顺时针"一致——因为我们投影时未翻转 y,SVG sweep=1(屏幕 CW)
12
- * 恰好等价 math 角度增加。
13
- *
14
- * 边界:`|Δ|=180°` 时 `largeArc=0`(半弧不算大弧);恰好同点 `|Δ|=0` 也返回 0。
15
- */
16
- export declare const arcSvgFlags: (startAngleDeg: number, endAngleDeg: number) => {
17
- largeArc: 0 | 1;
18
- sweep: 0 | 1;
19
- };
20
- /**
21
- * 弧的 bounding box 极值候选点:起点、终点,以及 `[startAngle, endAngle]` 区间
22
- * 里所有 90°·k 的基本方向(0°、90°、180°、270° 及其周期延拓)对应的圆周点。
23
- *
24
- * 这些点是计算视图框(viewBox)/ bbox 时必须考虑的候选——因为弧线投影到 x/y
25
- * 轴的极值只可能在弧端点或圆周上的轴向四点处出现。
26
- *
27
- * 说明:
28
- * - 接受 `endAngle < startAngle`(CW math 方向):以 `min..max` 区间扫描 90°·k;
29
- * 语义上是"无论扫描方向,此弧覆盖到的角度区间内的基本方向都算极值候选"。
30
- * - 跨 360°(如 270° → 450°)也按数值区间处理,正确收录中间穿越的 0°、90° 等。
31
- * - 不去重:若起点恰好落在 90°·k 上会同时出现两次,调用方根据需要再处理。
5
+ * 弧的 bbox 极值候选:起点、终点,加 [startAngle,endAngle] 内所有 90°·k 方向的圆周点
6
+ * @description 弧投影到 x/y 轴的极值只可能在弧端点或圆周轴向四点出现。endAngle < startAngle 时按 min..max 扫描;跨 360°(270→450)按数值区间正确处理;不去重——端角恰在 90°·k 上时调用方处理
32
7
  */
33
8
  export declare const arcBoundingPoints: (center: Position, radius: number, startAngleDeg: number, endAngleDeg: number) => Array<Position>;
34
9
  //# sourceMappingURL=arc.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"arc.d.ts","sourceRoot":"","sources":["../../../src/geometry/arc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAkBxC;;GAEG;AACH,eAAO,MAAM,WAAW,GACtB,QAAQ,QAAQ,EAChB,QAAQ,MAAM,EACd,UAAU,MAAM,KACf,QAMF,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,GACtB,eAAe,MAAM,EACrB,aAAa,MAAM,KAClB;IAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC;IAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAA;CAMjC,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,QAAQ,EAChB,QAAQ,MAAM,EACd,eAAe,MAAM,EACrB,aAAa,MAAM,KAClB,KAAK,CAAC,QAAQ,CAmBhB,CAAC"}
1
+ {"version":3,"file":"arc.d.ts","sourceRoot":"","sources":["../../../src/geometry/arc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAWxC,iDAAiD;AACjD,eAAO,MAAM,WAAW,GACtB,QAAQ,QAAQ,EAChB,QAAQ,MAAM,EACd,UAAU,MAAM,KACf,QAMF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,QAAQ,EAChB,QAAQ,MAAM,EACd,eAAe,MAAM,EACrB,aAAa,MAAM,KAClB,KAAK,CAAC,QAAQ,CAiBhB,CAAC"}
@@ -1,40 +1,13 @@
1
1
  //#region src/geometry/arc.ts
2
2
  var DEG_TO_RAD = Math.PI / 180;
3
- /**
4
- * 给定圆心、半径、角度(度,与 polar.toPosition 同约定),返回圆周上对应点。
5
- */
3
+ /** 圆心、半径、角度(度,与 polar.toPosition 同约定)→ 圆周上对应点 */
6
4
  var arcEndPoint = (center, radius, angleDeg) => {
7
5
  const rad = angleDeg * DEG_TO_RAD;
8
6
  return [center[0] + Math.cos(rad) * radius, center[1] + Math.sin(rad) * radius];
9
7
  };
10
8
  /**
11
- * 计算 SVG `<path>` A 命令需要的 large-arc-flag 与 sweep-flag。
12
- *
13
- * - `largeArc`:弧跨度 `|endAngle - startAngle|` 严格大于 180° 时为 1。
14
- * - `sweep`:`endAngle >= startAngle` 时为 1,否则为 0;与"角度增加方向 = 在
15
- * SVG 屏幕上视觉顺时针"一致——因为我们投影时未翻转 y,SVG sweep=1(屏幕 CW)
16
- * 恰好等价 math 角度增加。
17
- *
18
- * 边界:`|Δ|=180°` 时 `largeArc=0`(半弧不算大弧);恰好同点 `|Δ|=0` 也返回 0。
19
- */
20
- var arcSvgFlags = (startAngleDeg, endAngleDeg) => {
21
- return {
22
- largeArc: Math.abs(endAngleDeg - startAngleDeg) > 180 ? 1 : 0,
23
- sweep: endAngleDeg >= startAngleDeg ? 1 : 0
24
- };
25
- };
26
- /**
27
- * 弧的 bounding box 极值候选点:起点、终点,以及 `[startAngle, endAngle]` 区间
28
- * 里所有 90°·k 的基本方向(0°、90°、180°、270° 及其周期延拓)对应的圆周点。
29
- *
30
- * 这些点是计算视图框(viewBox)/ bbox 时必须考虑的候选——因为弧线投影到 x/y
31
- * 轴的极值只可能在弧端点或圆周上的轴向四点处出现。
32
- *
33
- * 说明:
34
- * - 接受 `endAngle < startAngle`(CW math 方向):以 `min..max` 区间扫描 90°·k;
35
- * 语义上是"无论扫描方向,此弧覆盖到的角度区间内的基本方向都算极值候选"。
36
- * - 跨 360°(如 270° → 450°)也按数值区间处理,正确收录中间穿越的 0°、90° 等。
37
- * - 不去重:若起点恰好落在 90°·k 上会同时出现两次,调用方根据需要再处理。
9
+ * 弧的 bbox 极值候选:起点、终点,加 [startAngle,endAngle] 内所有 90°·k 方向的圆周点
10
+ * @description 弧投影到 x/y 轴的极值只可能在弧端点或圆周轴向四点出现。endAngle < startAngle 时按 min..max 扫描;跨 360°(270→450)按数值区间正确处理;不去重——端角恰在 90°·k 上时调用方处理
38
11
  */
39
12
  var arcBoundingPoints = (center, radius, startAngleDeg, endAngleDeg) => {
40
13
  const points = [arcEndPoint(center, radius, startAngleDeg), arcEndPoint(center, radius, endAngleDeg)];
@@ -50,4 +23,4 @@ var arcBoundingPoints = (center, radius, startAngleDeg, endAngleDeg) => {
50
23
  return points;
51
24
  };
52
25
  //#endregion
53
- export { arcBoundingPoints, arcEndPoint, arcSvgFlags };
26
+ export { arcBoundingPoints, arcEndPoint };
@@ -1,18 +1,7 @@
1
1
  import { Position } from './point';
2
2
  /**
3
- * cubic Bezier 拟合 from→to 的弧形 bend
4
- *
5
- * 算法(ADR-0001):
6
- * 1. apex offset = chord × tan(bendAngle / 2)(沿 path 法向 left / right 偏移量)
7
- * 2. 控制点取 chord 1/3 / 2/3 处沿法向偏移;为让 cubic 在 t=0.5 处穿过 apex,
8
- * 控制点 offset = (4/3) × apex offset(推导:B(0.5) = midpoint + (3/4)·s·n)
9
- *
10
- * 法向定义(SVG y 向下,"left/right" 按视觉,不按数学):
11
- * chord direction = (dx, dy) / |chord|
12
- * visual-left normal = ( dy, -dx) / |chord| (从 from 看向 to,向左偏)
13
- * visual-right normal = (-dy, dx) / |chord|
14
- *
15
- * chord 长度为 0 时无方向可推,两个控制点都返回 from。
3
+ * cubic Bezier 拟合 from→to 的弧形 bend
4
+ * @description apex offset = chord × tan(bendAngle/2);控制点取 chord 1/3 与 2/3 处沿法向偏移,让 cubic 在 t=0.5 穿过 apex 故 ctlOffset = 4/3 × apexOffset。法向(screen y-down):visual-left=(dy,-dx)/|chord|,visual-right=(-dy,dx)/|chord|。chord=0 时两控制点都返回 from
16
5
  */
17
6
  export declare const bendControlPoints: (from: Position, to: Position, direction: "left" | "right", bendAngle: number) => [Position, Position];
18
7
  //# sourceMappingURL=bend.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bend.d.ts","sourceRoot":"","sources":["../../../src/geometry/bend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iBAAiB,GAC5B,MAAM,QAAQ,EACd,IAAI,QAAQ,EACZ,WAAW,MAAM,GAAG,OAAO,EAC3B,WAAW,MAAM,KAChB,CAAC,QAAQ,EAAE,QAAQ,CAgBrB,CAAC"}
1
+ {"version":3,"file":"bend.d.ts","sourceRoot":"","sources":["../../../src/geometry/bend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAC5B,MAAM,QAAQ,EACd,IAAI,QAAQ,EACZ,WAAW,MAAM,GAAG,OAAO,EAC3B,WAAW,MAAM,KAChB,CAAC,QAAQ,EAAE,QAAQ,CAgBrB,CAAC"}
@@ -1,18 +1,7 @@
1
1
  //#region src/geometry/bend.ts
2
2
  /**
3
- * cubic Bezier 拟合 from→to 的弧形 bend
4
- *
5
- * 算法(ADR-0001):
6
- * 1. apex offset = chord × tan(bendAngle / 2)(沿 path 法向 left / right 偏移量)
7
- * 2. 控制点取 chord 1/3 / 2/3 处沿法向偏移;为让 cubic 在 t=0.5 处穿过 apex,
8
- * 控制点 offset = (4/3) × apex offset(推导:B(0.5) = midpoint + (3/4)·s·n)
9
- *
10
- * 法向定义(SVG y 向下,"left/right" 按视觉,不按数学):
11
- * chord direction = (dx, dy) / |chord|
12
- * visual-left normal = ( dy, -dx) / |chord| (从 from 看向 to,向左偏)
13
- * visual-right normal = (-dy, dx) / |chord|
14
- *
15
- * chord 长度为 0 时无方向可推,两个控制点都返回 from。
3
+ * cubic Bezier 拟合 from→to 的弧形 bend
4
+ * @description apex offset = chord × tan(bendAngle/2);控制点取 chord 1/3 与 2/3 处沿法向偏移,让 cubic 在 t=0.5 穿过 apex 故 ctlOffset = 4/3 × apexOffset。法向(screen y-down):visual-left=(dy,-dx)/|chord|,visual-right=(-dy,dx)/|chord|。chord=0 时两控制点都返回 from
16
5
  */
17
6
  var bendControlPoints = (from, to, direction, bendAngle) => {
18
7
  const dx = to[0] - from[0];
@@ -1,20 +1,14 @@
1
1
  import { Position } from './point';
2
- /** 圆形:几何中心 (x, y) + 半径;与 Rect 一样支持可选旋转(圆对自身旋转无影响,预留供 anchor 命名一致性) */
2
+ import { RectAnchor } from './rect';
3
+ /** 圆形:几何中心 + 半径,预留旋转字段保持与 Rect 同形 API */
3
4
  export type Circle = {
4
- /** 圆心横坐标 */
5
5
  x: number;
6
- /** 圆心纵坐标 */
7
6
  y: number;
8
- /** 半径(user units) */
7
+ /** 半径 */
9
8
  radius: number;
10
- /** 绕几何中心旋转弧度;保留与 Rect 同形 API,圆形本身旋转后视觉不变 */
9
+ /** 绕中心旋转弧度(圆视觉不变,与 Rect 同形保留) */
11
10
  rotate?: number;
12
11
  };
13
- /**
14
- * 圆形 9 个标准 anchor 名集合,与 RECT_ANCHORS 同名同义。
15
- * 圆的"4 方位 + 4 对角"在圆周上等距分布(每 45°)。
16
- */
17
- export type CircleAnchor = 'center' | 'north' | 'south' | 'east' | 'west' | 'north-east' | 'north-west' | 'south-east' | 'south-west';
18
12
  /** 圆形相关基础工具 */
19
13
  export declare const circle: {
20
14
  /** 圆心 */
@@ -22,11 +16,8 @@ export declare const circle: {
22
16
  /** 判断点是否在圆内(含边界) */
23
17
  contains: (c: Circle, p: Position) => boolean;
24
18
  /** 9 个标准 anchor 之一的世界坐标 */
25
- anchor: (c: Circle, name: CircleAnchor) => Position;
26
- /**
27
- * 从圆心向 toward 方向画射线,求与圆周的交点。
28
- * 用于把 Path 端点贴到 Node 边界。
29
- */
19
+ anchor: (c: Circle, name: RectAnchor) => Position;
20
+ /** 从圆心向 toward 方向射线与圆周交点(Path 端点贴 Node 边界用) */
30
21
  boundaryPoint: (c: Circle, toward: Position) => Position;
31
22
  };
32
23
  //# sourceMappingURL=circle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"circle.d.ts","sourceRoot":"","sources":["../../../src/geometry/circle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,sEAAsE;AACtE,MAAM,MAAM,MAAM,GAAG;IACnB,YAAY;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,YAAY;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,qBAAqB;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAIF;;;GAGG;AACH,MAAM,MAAM,YAAY,GACpB,QAAQ,GACR,OAAO,GACP,OAAO,GACP,MAAM,GACN,MAAM,GACN,YAAY,GACZ,YAAY,GACZ,YAAY,GACZ,YAAY,CAAC;AAoBjB,eAAe;AACf,eAAO,MAAM,MAAM;IACjB,SAAS;gBACG,MAAM,KAAG,QAAQ;IAC7B,oBAAoB;kBACN,MAAM,KAAK,QAAQ,KAAG,OAAO;IAI3C,2BAA2B;gBACf,MAAM,QAAQ,YAAY,KAAG,QAAQ;IAsCjD;;;OAGG;uBACgB,MAAM,UAAU,QAAQ,KAAG,QAAQ;CAOvD,CAAC"}
1
+ {"version":3,"file":"circle.d.ts","sourceRoot":"","sources":["../../../src/geometry/circle.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEzC,yCAAyC;AACzC,MAAM,MAAM,MAAM,GAAG;IACnB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,SAAS;IACT,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAIF,eAAe;AACf,eAAO,MAAM,MAAM;IACjB,SAAS;gBACG,MAAM,KAAG,QAAQ;IAC7B,oBAAoB;kBACN,MAAM,KAAK,QAAQ,KAAG,OAAO;IAI3C,2BAA2B;gBACf,MAAM,QAAQ,UAAU,KAAG,QAAQ;IAsC/C,+CAA+C;uBAC5B,MAAM,UAAU,QAAQ,KAAG,QAAQ;CAOvD,CAAC"}