@retikz/core 0.1.0-alpha.1 → 0.1.0-alpha.3

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 (107) hide show
  1. package/dist/es/compile/compile.js +3 -3
  2. package/dist/es/compile/node.d.ts +32 -6
  3. package/dist/es/compile/node.d.ts.map +1 -1
  4. package/dist/es/compile/node.js +112 -28
  5. package/dist/es/compile/path.d.ts +3 -2
  6. package/dist/es/compile/path.d.ts.map +1 -1
  7. package/dist/es/compile/path.js +333 -14
  8. package/dist/es/compile/text-metrics.d.ts +2 -2
  9. package/dist/es/compile/text-metrics.d.ts.map +1 -1
  10. package/dist/es/geometry/arc.d.ts +34 -0
  11. package/dist/es/geometry/arc.d.ts.map +1 -0
  12. package/dist/es/geometry/arc.js +53 -0
  13. package/dist/es/geometry/bend.d.ts +18 -0
  14. package/dist/es/geometry/bend.d.ts.map +1 -0
  15. package/dist/es/geometry/bend.js +29 -0
  16. package/dist/es/geometry/index.d.ts +3 -0
  17. package/dist/es/geometry/index.d.ts.map +1 -1
  18. package/dist/es/geometry/segment.d.ts +38 -0
  19. package/dist/es/geometry/segment.d.ts.map +1 -0
  20. package/dist/es/geometry/segment.js +82 -0
  21. package/dist/es/index.d.ts +6 -6
  22. package/dist/es/index.d.ts.map +1 -1
  23. package/dist/es/index.js +5 -4
  24. package/dist/es/ir/node.d.ts +286 -6
  25. package/dist/es/ir/node.d.ts.map +1 -1
  26. package/dist/es/ir/node.js +71 -5
  27. package/dist/es/ir/path/path.d.ts +625 -15
  28. package/dist/es/ir/path/path.d.ts.map +1 -1
  29. package/dist/es/ir/path/path.js +22 -0
  30. package/dist/es/ir/path/step.d.ts +872 -20
  31. package/dist/es/ir/path/step.d.ts.map +1 -1
  32. package/dist/es/ir/path/step.js +86 -4
  33. package/dist/es/ir/path/target.d.ts +32 -2
  34. package/dist/es/ir/path/target.d.ts.map +1 -1
  35. package/dist/es/ir/path/target.js +7 -3
  36. package/dist/es/ir/scene.d.ts +1836 -52
  37. package/dist/es/ir/scene.d.ts.map +1 -1
  38. package/dist/es/parsers/index.d.ts +1 -0
  39. package/dist/es/parsers/index.d.ts.map +1 -1
  40. package/dist/es/parsers/parseTargetSugar.d.ts +3 -0
  41. package/dist/es/parsers/parseTargetSugar.d.ts.map +1 -0
  42. package/dist/es/parsers/parseTargetSugar.js +31 -0
  43. package/dist/es/parsers/parseWay.d.ts +131 -22
  44. package/dist/es/parsers/parseWay.d.ts.map +1 -1
  45. package/dist/es/parsers/parseWay.js +149 -17
  46. package/dist/es/primitive/ellipse.d.ts +2 -0
  47. package/dist/es/primitive/ellipse.d.ts.map +1 -1
  48. package/dist/es/primitive/path.d.ts +4 -0
  49. package/dist/es/primitive/path.d.ts.map +1 -1
  50. package/dist/es/primitive/rect.d.ts +2 -0
  51. package/dist/es/primitive/rect.d.ts.map +1 -1
  52. package/dist/es/primitive/text.d.ts +43 -13
  53. package/dist/es/primitive/text.d.ts.map +1 -1
  54. package/dist/lib/compile/compile.cjs +3 -3
  55. package/dist/lib/compile/node.cjs +112 -28
  56. package/dist/lib/compile/node.d.ts +32 -6
  57. package/dist/lib/compile/node.d.ts.map +1 -1
  58. package/dist/lib/compile/path.cjs +333 -14
  59. package/dist/lib/compile/path.d.ts +3 -2
  60. package/dist/lib/compile/path.d.ts.map +1 -1
  61. package/dist/lib/compile/text-metrics.d.ts +2 -2
  62. package/dist/lib/compile/text-metrics.d.ts.map +1 -1
  63. package/dist/lib/geometry/arc.cjs +55 -0
  64. package/dist/lib/geometry/arc.d.ts +34 -0
  65. package/dist/lib/geometry/arc.d.ts.map +1 -0
  66. package/dist/lib/geometry/bend.cjs +29 -0
  67. package/dist/lib/geometry/bend.d.ts +18 -0
  68. package/dist/lib/geometry/bend.d.ts.map +1 -0
  69. package/dist/lib/geometry/index.d.ts +3 -0
  70. package/dist/lib/geometry/index.d.ts.map +1 -1
  71. package/dist/lib/geometry/segment.cjs +88 -0
  72. package/dist/lib/geometry/segment.d.ts +38 -0
  73. package/dist/lib/geometry/segment.d.ts.map +1 -0
  74. package/dist/lib/index.cjs +16 -0
  75. package/dist/lib/index.d.ts +6 -6
  76. package/dist/lib/index.d.ts.map +1 -1
  77. package/dist/lib/ir/node.cjs +74 -4
  78. package/dist/lib/ir/node.d.ts +286 -6
  79. package/dist/lib/ir/node.d.ts.map +1 -1
  80. package/dist/lib/ir/path/path.cjs +22 -0
  81. package/dist/lib/ir/path/path.d.ts +625 -15
  82. package/dist/lib/ir/path/path.d.ts.map +1 -1
  83. package/dist/lib/ir/path/step.cjs +93 -3
  84. package/dist/lib/ir/path/step.d.ts +872 -20
  85. package/dist/lib/ir/path/step.d.ts.map +1 -1
  86. package/dist/lib/ir/path/target.cjs +8 -2
  87. package/dist/lib/ir/path/target.d.ts +32 -2
  88. package/dist/lib/ir/path/target.d.ts.map +1 -1
  89. package/dist/lib/ir/scene.d.ts +1836 -52
  90. package/dist/lib/ir/scene.d.ts.map +1 -1
  91. package/dist/lib/parsers/index.d.ts +1 -0
  92. package/dist/lib/parsers/index.d.ts.map +1 -1
  93. package/dist/lib/parsers/parseTargetSugar.cjs +31 -0
  94. package/dist/lib/parsers/parseTargetSugar.d.ts +3 -0
  95. package/dist/lib/parsers/parseTargetSugar.d.ts.map +1 -0
  96. package/dist/lib/parsers/parseWay.cjs +149 -17
  97. package/dist/lib/parsers/parseWay.d.ts +131 -22
  98. package/dist/lib/parsers/parseWay.d.ts.map +1 -1
  99. package/dist/lib/primitive/ellipse.d.ts +2 -0
  100. package/dist/lib/primitive/ellipse.d.ts.map +1 -1
  101. package/dist/lib/primitive/path.d.ts +4 -0
  102. package/dist/lib/primitive/path.d.ts.map +1 -1
  103. package/dist/lib/primitive/rect.d.ts +2 -0
  104. package/dist/lib/primitive/rect.d.ts.map +1 -1
  105. package/dist/lib/primitive/text.d.ts +43 -13
  106. package/dist/lib/primitive/text.d.ts.map +1 -1
  107. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"scene.d.ts","sourceRoot":"","sources":["../../../src/ir/scene.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAIrB,CAAC;AAEJ,gCAAgC;AAChC,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAElD,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkBrB,CAAC;AAEJ,4CAA4C;AAC5C,MAAM,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAE7C,qCAAqC;AACrC,eAAO,MAAM,kBAAkB,EAAG,CAAU,CAAC"}
1
+ {"version":3,"file":"scene.d.ts","sourceRoot":"","sources":["../../../src/ir/scene.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,3 @@
1
1
  export * from './parseWay';
2
+ export * from './parseTargetSugar';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/parsers/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/parsers/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { IRTarget } from '../ir';
2
+ export declare const parseTargetSugar: (input: unknown) => IRTarget;
3
+ //# sourceMappingURL=parseTargetSugar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseTargetSugar.d.ts","sourceRoot":"","sources":["../../../src/parsers/parseTargetSugar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAqBtC,eAAO,MAAM,gBAAgB,GAAI,OAAO,OAAO,KAAG,QAWjD,CAAC"}
@@ -0,0 +1,31 @@
1
+ //#region src/parsers/parseTargetSugar.ts
2
+ /**
3
+ * Sugar 字符串解析:识别 TikZ 风格的相对偏移字面量,把 `'+1,0'` /
4
+ * `'++1.5,-2'` 这类字符串转成 IR 的 { rel } / { relAccumulate } 对象;
5
+ * 其它形态(节点 id `'A'` / `'A.north'`、笛卡尔元组、极坐标对象、已经
6
+ * 是 rel/relAccumulate 对象)原样返回。
7
+ *
8
+ * 语法:
9
+ * - `'+<dx>,<dy>'` → `{ rel: [dx, dy] }` (TikZ `+` 语义,不更新 prevEnd)
10
+ * - `'++<dx>,<dy>'` → `{ relAccumulate: [dx, dy] }` (TikZ `++` 语义,累积更新)
11
+ * - `<dx>` / `<dy>`:可带正负号小数,如 `'+1,0'` / `'++1.5,-2.5'` / `'+ -3, 4'`
12
+ *
13
+ * 不匹配的字符串(如 `'A'` / `'A.north'` / `'A.30'`)原样返回——首字母为字母时
14
+ * 不会撞 `+` 前缀;含 `.` 但不含 `,` 的也不撞。
15
+ *
16
+ * 这是纯函数,没有 nodeIndex 依赖,住在 core/parsers,react adapter 与
17
+ * Draw way DSL 共用。
18
+ */
19
+ var REL_RE = /^(\+{1,2})\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)$/;
20
+ var parseTargetSugar = (input) => {
21
+ if (typeof input !== "string") return input;
22
+ const match = input.match(REL_RE);
23
+ if (!match) return input;
24
+ const plus = match[1];
25
+ const dx = Number(match[2]);
26
+ const dy = Number(match[3]);
27
+ if (plus === "++") return { relAccumulate: [dx, dy] };
28
+ return { rel: [dx, dy] };
29
+ };
30
+ //#endregion
31
+ export { parseTargetSugar };
@@ -1,12 +1,16 @@
1
- import { IRStep, IRTarget } from '../ir';
1
+ import { IRControlPoint, IRStep, IRStepLabel, IRTarget } from '../ir';
2
2
  /**
3
3
  * Sugar 层 way 数组的"关键字常量"。
4
4
  *
5
- * - `DrawWay.cycle`:闭合到 way 起点(way 元素位置)。**底层字符串故意取得很丑**
5
+ * - `DrawWay.Cycle`:闭合到 way 起点(way 元素位置)。**底层字符串故意取得很丑**
6
6
  * (`'retikz-keyword_cycle'`),保证不会与任何合理的节点 id 冲突;用户应当
7
- * 只通过 `DrawWay.cycle` 引用,不要直接写裸字面量。
8
- * - `DrawWay.hv`:折角算子,先水平后垂直(裸字面量 `'-|'`)。
9
- * - `DrawWay.vh`:折角算子,先垂直后水平(裸字面量 `'|-'`)。
7
+ * 只通过 `DrawWay.Cycle` 引用,不要直接写裸字面量。
8
+ * - `DrawWay.Hv`:折角算子,先水平后垂直(裸字面量 `'-|'`)。
9
+ * - `DrawWay.Vh`:折角算子,先垂直后水平(裸字面量 `'|-'`)。
10
+ * - `DrawWay.Relative` / `DrawWay.Accumulate`:相对偏移 way item 的 `type` 鉴别字段值。
11
+ * 配合 `{ position: [dx, dy], type: DrawWay.Relative | DrawWay.Accumulate }` 用——
12
+ * `Relative` 等价 TikZ `(+x, +y)`(不更新 prevEnd),`Accumulate` 等价 TikZ `(++x, ++y)`
13
+ * (累积更新)。底层字符串同样刻意写丑,保证不撞节点 id;用户只通过常量引用。
10
14
  *
11
15
  * 折角值(`-|` / `|-`)含特殊字符,与节点 id 不会冲突,因此**保留字面量与
12
16
  * 常量两种写法都合法**。way 数组里直接放 `'-|'` 或 `'|-'`(infix 算子)
@@ -21,37 +25,138 @@ export declare const DrawWay: {
21
25
  * 闭合 way 到起点,等价于 `<Step kind="cycle" />`(TikZ 同名保留字 / SVG `Z`)。
22
26
  * 底层字符串值刻意写丑以避开节点 id 冲突;不要硬编码这个字符串。
23
27
  */
24
- readonly cycle: "retikz-keyword_cycle";
28
+ readonly Cycle: "retikz-keyword_cycle";
25
29
  /** 折角:先水平后垂直(TikZ `-|`) */
26
- readonly hv: "-|";
30
+ readonly Hv: "-|";
27
31
  /** 折角:先垂直后水平(TikZ `|-`) */
28
- readonly vh: "|-";
32
+ readonly Vh: "|-";
33
+ /**
34
+ * 相对偏移 way item 的 `type` 字段值——非累积分支,等价 TikZ `(+x, +y)`。
35
+ * 解析为 IR `{ rel: position }`,**不**推进 prevEnd;多段链式偏移共享同一锚点。
36
+ */
37
+ readonly Relative: "retikz-keyword_relative";
38
+ /**
39
+ * 相对偏移 way item 的 `type` 字段值——累积分支,等价 TikZ `(++x, ++y)`。
40
+ * 解析为 IR `{ relAccumulate: position }`,每段累积更新 prevEnd。
41
+ */
42
+ readonly Accumulate: "retikz-keyword_accumulate";
43
+ };
44
+ /**
45
+ * way 折角算子的字面量类型。`DrawWay.Hv` / `DrawWay.Vh` 与裸字面量等价。
46
+ */
47
+ export type WayVia = typeof DrawWay.Hv | typeof DrawWay.Vh;
48
+ /**
49
+ * way 闭合关键字的字面量类型,由 `DrawWay.Cycle` 派生。
50
+ */
51
+ export type WayCycle = typeof DrawWay.Cycle;
52
+ /**
53
+ * 相对偏移 way item(sugar,TS-friendly):以**前一 step 终点**(首项无前段时回退到 [0, 0])
54
+ * 为基准的位移。`type: DrawWay.Relative` 不更新 prevEnd(TikZ `(+x, +y)`);
55
+ * `type: DrawWay.Accumulate` 累积更新(TikZ `(++x, ++y)`)。
56
+ *
57
+ * 与 sugar 字符串 `'+dx,dy'` / `'++dx,dy'` 等价,但对象形态在编辑器里能直接补全
58
+ * `position` / `type` 字段,TS 也能校验元组与鉴别字段值——更适合 IDE 协作。
59
+ *
60
+ * parseWay 在 sugar 层就地翻译为 IR `{ rel }` / `{ relAccumulate }`;IR 持久化
61
+ * 形态保持纯净,仍是带 `rel` / `relAccumulate` discriminator 的对象。
62
+ */
63
+ export type WayRelItem = {
64
+ position: [number, number];
65
+ type: typeof DrawWay.Relative | typeof DrawWay.Accumulate;
66
+ };
67
+ /**
68
+ * 二次贝塞尔算子(infix):与折角算子一样坐落两个 target 之间,把
69
+ * "上一项 → 下一项"那段改成 curve step。`curve` 字段携带控制点 `[x, y]`。
70
+ */
71
+ export type WayCurveOp = {
72
+ curve: IRControlPoint;
29
73
  };
30
74
  /**
31
- * way 折角算子的字面量类型。`DrawWay.hv` / `DrawWay.vh` 与裸字面量等价。
75
+ * 三次贝塞尔算子(infix):把"上一项 下一项"那段改成 cubic step。
76
+ * `cubic` 字段携带两个控制点 `[c1, c2]`。
32
77
  */
33
- export type WayVia = typeof DrawWay.hv | typeof DrawWay.vh;
78
+ export type WayCubicOp = {
79
+ cubic: [IRControlPoint, IRControlPoint];
80
+ };
81
+ /**
82
+ * 弧形简记算子(infix):把"上一项 → 下一项"那段改成 bend step。
83
+ * `bend` 字段是方向 `'left'` / `'right'`;`angle` 可选(缺省 30°)。
84
+ */
85
+ export type WayBendOp = {
86
+ bend: 'left' | 'right';
87
+ angle?: number;
88
+ };
89
+ /**
90
+ * 弧段算子(infix):以"上一项"为圆心,按起末角度 + 半径画弧;不消耗下一项
91
+ * (形状 step 没有 to 字段)。
92
+ *
93
+ * 注意与曲线 / 折角 infix 算子不同:way 数组里 `[..., target, { arc: {...} }, ...]`
94
+ * 时 arc 算子**只**消耗"前一个 target"作为圆心,**不**与"下一项"合并;
95
+ * 后续的 way item 仍按各自规则解析。
96
+ */
97
+ export type WayArcOp = {
98
+ arc: {
99
+ startAngle: number;
100
+ endAngle: number;
101
+ radius: number;
102
+ };
103
+ };
104
+ /** 整圆算子(infix):以"上一项"为圆心、给定半径,画整圆。pen 留圆心 */
105
+ export type WayCircleOp = {
106
+ circle: {
107
+ radius: number;
108
+ };
109
+ };
110
+ /** 整椭圆算子(infix):以"上一项"为圆心,给定 x/y 半径,画整椭圆。pen 留圆心 */
111
+ export type WayEllipseOp = {
112
+ ellipse: {
113
+ radiusX: number;
114
+ radiusY: number;
115
+ };
116
+ };
117
+ /**
118
+ * 边标注的 sugar 形态(ADR-0004):
119
+ * - 字符串:等价 `{ text: <string> }`,其它字段走默认(midway / above)
120
+ * - 对象:与 IR `step.label` 字面一致
121
+ */
122
+ export type WayLabel = IRStepLabel | string;
34
123
  /**
35
- * way 闭合关键字的字面量类型,由 `DrawWay.cycle` 派生。
124
+ * 边标注 prefix 算子(infix):修饰"下一段"——line / fold / curve / cubic / bend /
125
+ * arc / circle / ellipse 都可承载。挂在 way 中两个 way item 之间,下一个产生段的
126
+ * way item 把它消耗到自己的 IR step.label 上。
127
+ *
128
+ * `cycle` 不允许挂 label(schema 已禁),way 中"label op + cycle"的组合会抛错。
129
+ *
130
+ * 多个 label op 不能直接相邻,way 末尾未消费的 label op 同样抛错——保持"标注总有
131
+ * 段可挂"。
36
132
  */
37
- export type WayCycle = typeof DrawWay.cycle;
133
+ export type WayLabelOp = {
134
+ label: WayLabel;
135
+ };
38
136
  /**
39
137
  * Sugar 层的 way 数组 DSL 元素。
40
138
  *
41
- * v0.1.0-alpha.1 接受五种形态:
139
+ * 接受十二种形态:
42
140
  * - 节点 id 字符串:`'A'` → line(首项时为 move)
43
141
  * - 笛卡尔坐标:`[x, y]` → line
44
142
  * - 极坐标:`{ origin?, angle, radius }` → line
45
- * - 折角算子:`'-|'` / `'|-'`(或 `DrawWay.hv` / `DrawWay.vh`)→ 当前项 +
143
+ * - 相对偏移对象(sugar,TS-friendly):`{ position: [dx, dy], type: DrawWay.Relative | DrawWay.Accumulate }`
144
+ * 翻译为 IR `{ rel }` / `{ relAccumulate }`;与裸字符串 `'+dx,dy'` / `'++dx,dy'` 等价
145
+ * - 折角算子:`'-|'` / `'|-'`(或 `DrawWay.Hv` / `DrawWay.Vh`)→ 当前项 +
46
146
  * **下一项**合并成一个折角 step(与 TikZ 的 `(A) -| (B)` infix 写法对齐)
47
- * - 闭合关键字:`DrawWay.cycle` → cycle(闭合到起点)
48
- *
49
- * 后续会加:相对位移(`{ rel: [x, y] }`)、curve / cubic 等。
147
+ * - 闭合关键字:`DrawWay.Cycle` → cycle(闭合到起点)
148
+ * - 二次贝塞尔算子(infix):`{ curve: [cx, cy] }`,与下一项合并为 curve step
149
+ * - 三次贝塞尔算子(infix):`{ cubic: [[c1x, c1y], [c2x, c2y]] }`,与下一项合并为 cubic step
150
+ * - 弧形简记算子(infix):`{ bend: 'left' | 'right', angle?: number }`,与下一项合并为 bend step
151
+ * - 弧段算子(infix):`{ arc: { startAngle, endAngle, radius } }`,以"上一项"为圆心,**不**消耗下一项
152
+ * - 整圆算子(infix):`{ circle: { radius } }`,以"上一项"为圆心,**不**消耗下一项
153
+ * - 整椭圆算子(infix):`{ ellipse: { radiusX, radiusY } }`,以"上一项"为圆心,**不**消耗下一项
50
154
  *
51
- * 注意:闭合刻意只走 `DrawWay.cycle`(底层字符串是 `'retikz-keyword_cycle'`),
52
- * 这样裸字符串 `'cycle'` 仍可作为正常节点 id 使用。
155
+ * 注意:闭合刻意只走 `DrawWay.Cycle`(底层字符串是 `'retikz-keyword_cycle'`),
156
+ * 这样裸字符串 `'cycle'` 仍可作为正常节点 id 使用。相对偏移的 `Relative` /
157
+ * `Accumulate` 同理刻意写丑——避免与节点 id / Position 形态撞结构。
53
158
  */
54
- export type WayItem = IRTarget | WayVia | WayCycle;
159
+ export type WayItem = IRTarget | WayRelItem | WayVia | WayCycle | WayCurveOp | WayCubicOp | WayBendOp | WayArcOp | WayCircleOp | WayEllipseOp | WayLabelOp;
55
160
  /** way DSL 数组:sugar `<Draw way={...}>` 接受的输入形态 */
56
161
  export type WayDSL = Array<WayItem>;
57
162
  /**
@@ -60,10 +165,14 @@ export type WayDSL = Array<WayItem>;
60
165
  * - 第一个元素始终是 move:取 way[0] 的目标点;若 way[0] 是 cycle / via 算子等
61
166
  * 非 target 项,则降级到原点 `[0, 0]`(容错)。
62
167
  * - 后续元素:
63
- * - 普通 target → line
64
- * - `DrawWay.cycle` → cycle 步
168
+ * - 普通 target / `WayRelItem` → line
169
+ * - `DrawWay.Cycle` → cycle 步
65
170
  * - `'-|'` / `'|-'` → 与**下一项**合并成 fold 步;操作符在 way 末尾或后
66
171
  * 接非 target 项时抛错
172
+ * - curve / cubic / bend infix 算子 → 与**下一项**合并成对应 step
173
+ * - arc / circle / ellipse infix 算子 → 单独成 step(不消耗下一项)
174
+ * - `{ label }` infix 算子(ADR-0004)→ 修饰**下一段**——挂在该段的
175
+ * `step.label` 上;连续 label / 末尾 label / cycle 上的 label 均抛错
67
176
  *
68
177
  * 这是纯函数,住在 core,被各框架 adapter 的 Sugar 组件复用。
69
178
  */
@@ -1 +1 @@
1
- {"version":3,"file":"parseWay.d.ts","sourceRoot":"","sources":["../../../src/parsers/parseWay.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAKV,MAAM,EACN,QAAQ,EACT,MAAM,OAAO,CAAC;AAEf;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,OAAO;IAClB;;;OAGG;;IAEH,2BAA2B;;IAE3B,2BAA2B;;CAEnB,CAAC;AAEX;;GAEG;AACH,MAAM,MAAM,MAAM,GAAG,OAAO,OAAO,CAAC,EAAE,GAAG,OAAO,OAAO,CAAC,EAAE,CAAC;AAE3D;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,OAAO,CAAC,KAAK,CAAC;AAE5C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEnD,kDAAkD;AAClD,MAAM,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;AAapC;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,KAAG,KAAK,CAAC,MAAM,CAoClD,CAAC"}
1
+ {"version":3,"file":"parseWay.d.ts","sourceRoot":"","sources":["../../../src/parsers/parseWay.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAIV,cAAc,EAQd,MAAM,EACN,WAAW,EACX,QAAQ,EACT,MAAM,OAAO,CAAC;AAGf;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,OAAO;IAClB;;;OAGG;;IAEH,2BAA2B;;IAE3B,2BAA2B;;IAE3B;;;OAGG;;IAEH;;;OAGG;;CAEK,CAAC;AAEX;;GAEG;AACH,MAAM,MAAM,MAAM,GAAG,OAAO,OAAO,CAAC,EAAE,GAAG,OAAO,OAAO,CAAC,EAAE,CAAC;AAE3D;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,OAAO,CAAC,KAAK,CAAC;AAE5C;;;;;;;;;;GAUG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,IAAI,EAAE,OAAO,OAAO,CAAC,QAAQ,GAAG,OAAO,OAAO,CAAC,UAAU,CAAC;CAC3D,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG;IAAE,KAAK,EAAE,cAAc,CAAA;CAAE,CAAC;AAEnD;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG;IAAE,KAAK,EAAE,CAAC,cAAc,EAAE,cAAc,CAAC,CAAA;CAAE,CAAC;AAErE;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,GAAG,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/D,CAAC;AAEF,6CAA6C;AAC7C,MAAM,MAAM,WAAW,GAAG;IAAE,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAEzD,oDAAoD;AACpD,MAAM,MAAM,YAAY,GAAG;IAAE,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAE7E;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,MAAM,CAAC;AAE5C;;;;;;;;;GASG;AACH,MAAM,MAAM,UAAU,GAAG;IAAE,KAAK,EAAE,QAAQ,CAAA;CAAE,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,MAAM,OAAO,GACf,QAAQ,GACR,UAAU,GACV,MAAM,GACN,QAAQ,GACR,UAAU,GACV,UAAU,GACV,SAAS,GACT,QAAQ,GACR,WAAW,GACX,YAAY,GACZ,UAAU,CAAC;AAEf,kDAAkD;AAClD,MAAM,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;AAwEpC;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,KAAG,KAAK,CAAC,MAAM,CAoKlD,CAAC"}
@@ -1,12 +1,17 @@
1
+ import { parseTargetSugar } from "./parseTargetSugar.js";
1
2
  //#region src/parsers/parseWay.ts
2
3
  /**
3
4
  * Sugar 层 way 数组的"关键字常量"。
4
5
  *
5
- * - `DrawWay.cycle`:闭合到 way 起点(way 元素位置)。**底层字符串故意取得很丑**
6
+ * - `DrawWay.Cycle`:闭合到 way 起点(way 元素位置)。**底层字符串故意取得很丑**
6
7
  * (`'retikz-keyword_cycle'`),保证不会与任何合理的节点 id 冲突;用户应当
7
- * 只通过 `DrawWay.cycle` 引用,不要直接写裸字面量。
8
- * - `DrawWay.hv`:折角算子,先水平后垂直(裸字面量 `'-|'`)。
9
- * - `DrawWay.vh`:折角算子,先垂直后水平(裸字面量 `'|-'`)。
8
+ * 只通过 `DrawWay.Cycle` 引用,不要直接写裸字面量。
9
+ * - `DrawWay.Hv`:折角算子,先水平后垂直(裸字面量 `'-|'`)。
10
+ * - `DrawWay.Vh`:折角算子,先垂直后水平(裸字面量 `'|-'`)。
11
+ * - `DrawWay.Relative` / `DrawWay.Accumulate`:相对偏移 way item 的 `type` 鉴别字段值。
12
+ * 配合 `{ position: [dx, dy], type: DrawWay.Relative | DrawWay.Accumulate }` 用——
13
+ * `Relative` 等价 TikZ `(+x, +y)`(不更新 prevEnd),`Accumulate` 等价 TikZ `(++x, ++y)`
14
+ * (累积更新)。底层字符串同样刻意写丑,保证不撞节点 id;用户只通过常量引用。
10
15
  *
11
16
  * 折角值(`-|` / `|-`)含特殊字符,与节点 id 不会冲突,因此**保留字面量与
12
17
  * 常量两种写法都合法**。way 数组里直接放 `'-|'` 或 `'|-'`(infix 算子)
@@ -21,18 +26,50 @@ var DrawWay = {
21
26
  * 闭合 way 到起点,等价于 `<Step kind="cycle" />`(TikZ 同名保留字 / SVG `Z`)。
22
27
  * 底层字符串值刻意写丑以避开节点 id 冲突;不要硬编码这个字符串。
23
28
  */
24
- cycle: "retikz-keyword_cycle",
29
+ Cycle: "retikz-keyword_cycle",
25
30
  /** 折角:先水平后垂直(TikZ `-|`) */
26
- hv: "-|",
31
+ Hv: "-|",
27
32
  /** 折角:先垂直后水平(TikZ `|-`) */
28
- vh: "|-"
33
+ Vh: "|-",
34
+ /**
35
+ * 相对偏移 way item 的 `type` 字段值——非累积分支,等价 TikZ `(+x, +y)`。
36
+ * 解析为 IR `{ rel: position }`,**不**推进 prevEnd;多段链式偏移共享同一锚点。
37
+ */
38
+ Relative: "retikz-keyword_relative",
39
+ /**
40
+ * 相对偏移 way item 的 `type` 字段值——累积分支,等价 TikZ `(++x, ++y)`。
41
+ * 解析为 IR `{ relAccumulate: position }`,每段累积更新 prevEnd。
42
+ */
43
+ Accumulate: "retikz-keyword_accumulate"
44
+ };
45
+ var isWayCycle = (item) => item === DrawWay.Cycle;
46
+ var isWayVia = (item) => item === DrawWay.Hv || item === DrawWay.Vh;
47
+ var isPlainObject = (item) => typeof item === "object" && item !== null && !Array.isArray(item);
48
+ var isWayRelItem = (item) => isPlainObject(item) && "position" in item && "type" in item;
49
+ var isWayCurveOp = (item) => isPlainObject(item) && "curve" in item;
50
+ var isWayCubicOp = (item) => isPlainObject(item) && "cubic" in item;
51
+ var isWayBendOp = (item) => isPlainObject(item) && "bend" in item;
52
+ var isWayCurveLike = (item) => isWayCurveOp(item) || isWayCubicOp(item) || isWayBendOp(item);
53
+ var isWayArcOp = (item) => isPlainObject(item) && "arc" in item;
54
+ var isWayCircleOp = (item) => isPlainObject(item) && "circle" in item;
55
+ var isWayEllipseOp = (item) => isPlainObject(item) && "ellipse" in item;
56
+ var isWayShapeOp = (item) => isWayArcOp(item) || isWayCircleOp(item) || isWayEllipseOp(item);
57
+ var isWayLabelOp = (item) => isPlainObject(item) && "label" in item;
58
+ var isWayOperator = (item) => isWayCycle(item) || isWayVia(item) || isWayCurveLike(item) || isWayShapeOp(item) || isWayLabelOp(item);
59
+ /** sugar 字符串 / 对象都映射到 IR `step.label`:字符串 = `{ text: s }`,对象原样取 */
60
+ var normalizeLabel = (l) => typeof l === "string" ? { text: l } : { ...l };
61
+ /**
62
+ * 把 sugar 对象形态 `{ position, type }` 翻译为 IR `{ rel } | { relAccumulate }`;
63
+ * 其它形态原样返回。在每个 parseTargetSugar 入口前调用,集中处理。
64
+ */
65
+ var desugarRelItem = (item) => {
66
+ if (!isWayRelItem(item)) return item;
67
+ return item.type === DrawWay.Accumulate ? { relAccumulate: item.position } : { rel: item.position };
29
68
  };
30
- var isWayCycle = (item) => item === DrawWay.cycle;
31
- var isWayVia = (item) => item === DrawWay.hv || item === DrawWay.vh;
32
69
  /** 把 WayItem 归约为它的"目标点"——target 直接返回;算子/关键字返回 null */
33
70
  var targetOf = (item) => {
34
- if (isWayCycle(item) || isWayVia(item)) return null;
35
- return item;
71
+ if (isWayOperator(item)) return null;
72
+ return desugarRelItem(item);
36
73
  };
37
74
  /**
38
75
  * 把 way 数组翻译为 IRStep 序列。
@@ -40,25 +77,44 @@ var targetOf = (item) => {
40
77
  * - 第一个元素始终是 move:取 way[0] 的目标点;若 way[0] 是 cycle / via 算子等
41
78
  * 非 target 项,则降级到原点 `[0, 0]`(容错)。
42
79
  * - 后续元素:
43
- * - 普通 target → line
44
- * - `DrawWay.cycle` → cycle 步
80
+ * - 普通 target / `WayRelItem` → line
81
+ * - `DrawWay.Cycle` → cycle 步
45
82
  * - `'-|'` / `'|-'` → 与**下一项**合并成 fold 步;操作符在 way 末尾或后
46
83
  * 接非 target 项时抛错
84
+ * - curve / cubic / bend infix 算子 → 与**下一项**合并成对应 step
85
+ * - arc / circle / ellipse infix 算子 → 单独成 step(不消耗下一项)
86
+ * - `{ label }` infix 算子(ADR-0004)→ 修饰**下一段**——挂在该段的
87
+ * `step.label` 上;连续 label / 末尾 label / cycle 上的 label 均抛错
47
88
  *
48
89
  * 这是纯函数,住在 core,被各框架 adapter 的 Sugar 组件复用。
49
90
  */
50
91
  var parseWay = (way) => {
51
92
  if (way.length < 2) throw new Error("parseWay: way must contain at least 2 items");
52
93
  const out = [];
94
+ /** ADR-0004:当前未消费的 label 算子结果——下一个产生段的 way item 消耗它。 */
95
+ let pendingLabel;
96
+ const consumeLabel = () => {
97
+ const l = pendingLabel;
98
+ pendingLabel = void 0;
99
+ return l;
100
+ };
101
+ if (isWayLabelOp(way[0])) throw new Error(`parseWay: way[0] must be a target (move start), got label operator`);
102
+ const rawMove = targetOf(way[0]);
53
103
  const moveStep = {
54
104
  type: "step",
55
105
  kind: "move",
56
- to: targetOf(way[0]) ?? [0, 0]
106
+ to: rawMove === null ? [0, 0] : parseTargetSugar(rawMove)
57
107
  };
58
108
  out.push(moveStep);
59
109
  for (let i = 1; i < way.length; i++) {
60
110
  const item = way[i];
111
+ if (isWayLabelOp(item)) {
112
+ if (pendingLabel) throw new Error(`parseWay: label operator at index ${i} cannot directly follow another label operator`);
113
+ pendingLabel = normalizeLabel(item.label);
114
+ continue;
115
+ }
61
116
  if (isWayCycle(item)) {
117
+ if (pendingLabel) throw new Error(`parseWay: cycle step cannot carry a label (label operator at index ${i - 1})`);
62
118
  out.push({
63
119
  type: "step",
64
120
  kind: "cycle"
@@ -68,24 +124,100 @@ var parseWay = (way) => {
68
124
  if (isWayVia(item)) {
69
125
  if (i + 1 >= way.length) throw new Error(`parseWay: via operator '${item}' at end of way must be followed by a target`);
70
126
  const next = way[i + 1];
71
- if (isWayCycle(next) || isWayVia(next)) throw new Error(`parseWay: via operator '${item}' must be followed by a target, got '${String(next)}'`);
127
+ if (isWayOperator(next)) throw new Error(`parseWay: via operator '${item}' must be followed by a target, got '${String(next)}'`);
72
128
  const fold = {
73
129
  type: "step",
74
130
  kind: "step",
75
131
  via: item,
76
- to: next
132
+ to: parseTargetSugar(desugarRelItem(next))
77
133
  };
134
+ const label = consumeLabel();
135
+ if (label) fold.label = label;
78
136
  out.push(fold);
79
137
  i++;
80
138
  continue;
81
139
  }
140
+ if (isWayCurveLike(item)) {
141
+ if (i + 1 >= way.length) throw new Error(`parseWay: curve operator at end of way must be followed by a target`);
142
+ const next = way[i + 1];
143
+ if (isWayOperator(next)) throw new Error(`parseWay: curve operator must be followed by a target, got operator/keyword`);
144
+ const target = parseTargetSugar(desugarRelItem(next));
145
+ const label = consumeLabel();
146
+ if (isWayCurveOp(item)) {
147
+ const curve = {
148
+ type: "step",
149
+ kind: "curve",
150
+ to: target,
151
+ control: item.curve
152
+ };
153
+ if (label) curve.label = label;
154
+ out.push(curve);
155
+ } else if (isWayCubicOp(item)) {
156
+ const cubic = {
157
+ type: "step",
158
+ kind: "cubic",
159
+ to: target,
160
+ control1: item.cubic[0],
161
+ control2: item.cubic[1]
162
+ };
163
+ if (label) cubic.label = label;
164
+ out.push(cubic);
165
+ } else {
166
+ const bend = {
167
+ type: "step",
168
+ kind: "bend",
169
+ to: target,
170
+ bendDirection: item.bend
171
+ };
172
+ if (item.angle !== void 0) bend.bendAngle = item.angle;
173
+ if (label) bend.label = label;
174
+ out.push(bend);
175
+ }
176
+ i++;
177
+ continue;
178
+ }
179
+ if (isWayShapeOp(item)) {
180
+ const label = consumeLabel();
181
+ if (isWayArcOp(item)) {
182
+ const arc = {
183
+ type: "step",
184
+ kind: "arc",
185
+ startAngle: item.arc.startAngle,
186
+ endAngle: item.arc.endAngle,
187
+ radius: item.arc.radius
188
+ };
189
+ if (label) arc.label = label;
190
+ out.push(arc);
191
+ } else if (isWayCircleOp(item)) {
192
+ const circle = {
193
+ type: "step",
194
+ kind: "circlePath",
195
+ radius: item.circle.radius
196
+ };
197
+ if (label) circle.label = label;
198
+ out.push(circle);
199
+ } else {
200
+ const ellipse = {
201
+ type: "step",
202
+ kind: "ellipsePath",
203
+ radiusX: item.ellipse.radiusX,
204
+ radiusY: item.ellipse.radiusY
205
+ };
206
+ if (label) ellipse.label = label;
207
+ out.push(ellipse);
208
+ }
209
+ continue;
210
+ }
82
211
  const lineStep = {
83
212
  type: "step",
84
213
  kind: "line",
85
- to: item
214
+ to: parseTargetSugar(desugarRelItem(item))
86
215
  };
216
+ const label = consumeLabel();
217
+ if (label) lineStep.label = label;
87
218
  out.push(lineStep);
88
219
  }
220
+ if (pendingLabel) throw new Error(`parseWay: label operator at end of way must be followed by a step`);
89
221
  return out;
90
222
  };
91
223
  //#endregion
@@ -22,6 +22,8 @@ export type EllipsePrim = {
22
22
  fillOpacity?: number;
23
23
  /** 描边色 */
24
24
  stroke?: string;
25
+ /** 描边透明度 0~1(SVG stroke-opacity) */
26
+ strokeOpacity?: number;
25
27
  /** 描边宽度 */
26
28
  strokeWidth?: number;
27
29
  /** SVG stroke-dasharray 模式 */
@@ -1 +1 @@
1
- {"version":3,"file":"ellipse.d.ts","sourceRoot":"","sources":["../../../src/primitive/ellipse.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,YAAY;IACZ,IAAI,EAAE,SAAS,CAAC;IAChB,YAAY;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,YAAY;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,WAAW;IACX,EAAE,EAAE,MAAM,CAAC;IACX,WAAW;IACX,EAAE,EAAE,MAAM,CAAC;IACX,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU;IACV,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU;IACV,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8BAA8B;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC"}
1
+ {"version":3,"file":"ellipse.d.ts","sourceRoot":"","sources":["../../../src/primitive/ellipse.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,YAAY;IACZ,IAAI,EAAE,SAAS,CAAC;IAChB,YAAY;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,YAAY;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,WAAW;IACX,EAAE,EAAE,MAAM,CAAC;IACX,WAAW;IACX,EAAE,EAAE,MAAM,CAAC;IACX,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU;IACV,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU;IACV,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8BAA8B;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC"}
@@ -7,10 +7,14 @@ export type PathPrim = {
7
7
  d: string;
8
8
  /** 填充色;不填表示不填充 */
9
9
  fill?: string;
10
+ /** 填充透明度 0~1 */
11
+ fillOpacity?: number;
10
12
  /** SVG fill-rule:`nonzero`(默认)/ `evenodd`(环形 / 孔洞场景) */
11
13
  fillRule?: 'nonzero' | 'evenodd';
12
14
  /** 描边色 */
13
15
  stroke?: string;
16
+ /** 描边透明度 0~1(SVG stroke-opacity) */
17
+ strokeOpacity?: number;
14
18
  /** 描边宽度 */
15
19
  strokeWidth?: number;
16
20
  /** SVG stroke-dasharray 模式 */
@@ -1 +1 @@
1
- {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../../src/primitive/path.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,mDAAmD;AACnD,MAAM,MAAM,QAAQ,GAAG;IACrB,YAAY;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,CAAC,EAAE,MAAM,CAAC;IACV,kBAAkB;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,QAAQ,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IACjC,UAAU;IACV,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8BAA8B;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW;IACX,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC5C,WAAW;IACX,cAAc,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAC7C,2BAA2B;IAC3B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,gBAAgB;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC"}
1
+ {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../../src/primitive/path.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,mDAAmD;AACnD,MAAM,MAAM,QAAQ,GAAG;IACrB,YAAY;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,qBAAqB;IACrB,CAAC,EAAE,MAAM,CAAC;IACV,kBAAkB;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IACjC,UAAU;IACV,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8BAA8B;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW;IACX,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC5C,WAAW;IACX,cAAc,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IAC7C,2BAA2B;IAC3B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,gBAAgB;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC"}
@@ -16,6 +16,8 @@ export type RectPrim = {
16
16
  fillOpacity?: number;
17
17
  /** 描边色 */
18
18
  stroke?: string;
19
+ /** 描边透明度 0~1(SVG stroke-opacity) */
20
+ strokeOpacity?: number;
19
21
  /** 描边宽度 */
20
22
  strokeWidth?: number;
21
23
  /** SVG stroke-dasharray 模式 */
@@ -1 +1 @@
1
- {"version":3,"file":"rect.d.ts","sourceRoot":"","sources":["../../../src/primitive/rect.ts"],"names":[],"mappings":"AAAA,WAAW;AACX,MAAM,MAAM,QAAQ,GAAG;IACrB,YAAY;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,aAAa;IACb,CAAC,EAAE,MAAM,CAAC;IACV,aAAa;IACb,CAAC,EAAE,MAAM,CAAC;IACV,WAAW;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW;IACX,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU;IACV,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8BAA8B;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wBAAwB;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC"}
1
+ {"version":3,"file":"rect.d.ts","sourceRoot":"","sources":["../../../src/primitive/rect.ts"],"names":[],"mappings":"AAAA,WAAW;AACX,MAAM,MAAM,QAAQ,GAAG;IACrB,YAAY;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,aAAa;IACb,CAAC,EAAE,MAAM,CAAC;IACV,aAAa;IACb,CAAC,EAAE,MAAM,CAAC;IACV,WAAW;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW;IACX,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU;IACV,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8BAA8B;IAC9B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,wBAAwB;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC"}
@@ -1,32 +1,62 @@
1
- /** 文本原语;measuredWidth/Height 由 Scene 编译阶段算好,下游直接信任 */
1
+ /**
2
+ * 行级字段——`<tspan>` 上可独立指定的属性。
3
+ * 块级(TextPrim 顶层)属性是默认值;行级未填字段走块级默认。
4
+ */
5
+ export type TextLine = {
6
+ /** 行内容(必填) */
7
+ text: string;
8
+ /** 行字号;未填走 TextPrim.fontSize */
9
+ fontSize?: number;
10
+ /** 行字体族;未填走 TextPrim.fontFamily */
11
+ fontFamily?: string;
12
+ /** 行字重;未填走 TextPrim.fontWeight */
13
+ fontWeight?: string | number;
14
+ /** 行字形;未填走 TextPrim.fontStyle */
15
+ fontStyle?: 'normal' | 'italic' | 'oblique';
16
+ /** 行颜色;未填走 TextPrim.fill */
17
+ fill?: string;
18
+ /** 行透明度 0~1;未填走 TextPrim.opacity */
19
+ opacity?: number;
20
+ };
21
+ /**
22
+ * 文本原语;measuredWidth / Height 由 Scene 编译阶段算好,下游直接信任。
23
+ *
24
+ * 多行:`lines` 至少 1 行;renderer 把每行画为 `<tspan>`,按 `lineHeight` 堆叠,
25
+ * 整块根据 `baseline` 在 (x, y) 锚点上下居中 / 顶 / 底对齐。
26
+ *
27
+ * 顶层 `fontSize` / `fontFamily` / `fontWeight` / `fontStyle` / `fill` / `opacity` 是默认值;
28
+ * 单行可在 `TextLine` 上覆盖(仅生效于该行 `<tspan>`)。
29
+ */
2
30
  export type TextPrim = {
3
31
  /** 类型判别符 */
4
32
  type: 'text';
5
- /** 锚点横坐标(具体含义由 align 决定) */
33
+ /** 锚点横坐标(具体含义由 align 决定);多行下作为每行 `<tspan>` 的 x */
6
34
  x: number;
7
35
  /** 锚点纵坐标(具体含义由 baseline 决定) */
8
36
  y: number;
9
- /** 文本内容 */
10
- content: string;
11
- /** 字号 */
37
+ /** 文本行(至少 1 行);单行节点也用 `[{ text: 'Hello' }]` 形式 */
38
+ lines: Array<TextLine>;
39
+ /** 块级默认字号 */
12
40
  fontSize: number;
13
- /** 字体族 */
41
+ /** 块级默认字体族 */
14
42
  fontFamily?: string;
15
- /** 字重 */
43
+ /** 块级默认字重 */
16
44
  fontWeight?: string | number;
17
- /** 字形 */
18
- fontStyle?: 'normal' | 'italic';
45
+ /** 块级默认字形 */
46
+ fontStyle?: 'normal' | 'italic' | 'oblique';
19
47
  /** 水平对齐:start / middle / end 锚点位置 */
20
48
  align: 'start' | 'middle' | 'end';
21
49
  /** 垂直基线对齐方式 */
22
50
  baseline: 'top' | 'middle' | 'bottom' | 'alphabetic';
23
- /** 编译期算好的文字宽度(user units */
51
+ /** 行高(user units);多行下相邻 `<tspan>` 的垂直距离 */
52
+ lineHeight: number;
53
+ /** 编译期算好的整块文字宽度(user units,= max line width) */
24
54
  measuredWidth: number;
25
- /** 编译期算好的文字高度(user units) */
55
+ /** 编译期算好的整块文字高度(user units,≈ lines × lineHeight) */
26
56
  measuredHeight: number;
27
- /** 文字颜色 */
57
+ /** 块级默认文字颜色 */
28
58
  fill?: string;
29
- /** 整体透明度 0~1 */
59
+ /** 块级默认透明度 0~1 */
30
60
  opacity?: number;
31
61
  };
32
62
  //# sourceMappingURL=text.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/primitive/text.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,MAAM,MAAM,QAAQ,GAAG;IACrB,YAAY;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,4BAA4B;IAC5B,CAAC,EAAE,MAAM,CAAC;IACV,+BAA+B;IAC/B,CAAC,EAAE,MAAM,CAAC;IACV,WAAW;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS;IACT,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU;IACV,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS;IACT,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,SAAS;IACT,SAAS,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAChC,qCAAqC;IACrC,KAAK,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;IAClC,eAAe;IACf,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,CAAC;IACrD,6BAA6B;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,6BAA6B;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC"}
1
+ {"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/primitive/text.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,cAAc;IACd,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,iCAAiC;IACjC,SAAS,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC5C,4BAA4B;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,YAAY;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,CAAC,EAAE,MAAM,CAAC;IACV,+BAA+B;IAC/B,CAAC,EAAE,MAAM,CAAC;IACV,kDAAkD;IAClD,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACvB,aAAa;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa;IACb,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,aAAa;IACb,SAAS,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC5C,qCAAqC;IACrC,KAAK,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;IAClC,eAAe;IACf,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,CAAC;IACrD,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,aAAa,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kBAAkB;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC"}