@retikz/core 0.1.0-alpha.2 → 0.1.0-alpha.4
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.
- package/dist/es/compile/compile.d.ts +6 -0
- package/dist/es/compile/compile.d.ts.map +1 -1
- package/dist/es/compile/compile.js +33 -4
- package/dist/es/compile/node.d.ts +23 -2
- package/dist/es/compile/node.d.ts.map +1 -1
- package/dist/es/compile/node.js +94 -4
- package/dist/es/compile/path.d.ts +3 -2
- package/dist/es/compile/path.d.ts.map +1 -1
- package/dist/es/compile/path.js +333 -14
- package/dist/es/compile/position.d.ts +8 -5
- package/dist/es/compile/position.d.ts.map +1 -1
- package/dist/es/compile/position.js +32 -5
- package/dist/es/geometry/arc.d.ts +34 -0
- package/dist/es/geometry/arc.d.ts.map +1 -0
- package/dist/es/geometry/arc.js +53 -0
- package/dist/es/geometry/bend.d.ts +18 -0
- package/dist/es/geometry/bend.d.ts.map +1 -0
- package/dist/es/geometry/bend.js +29 -0
- package/dist/es/geometry/index.d.ts +3 -0
- package/dist/es/geometry/index.d.ts.map +1 -1
- package/dist/es/geometry/segment.d.ts +38 -0
- package/dist/es/geometry/segment.d.ts.map +1 -0
- package/dist/es/geometry/segment.js +82 -0
- package/dist/es/index.d.ts +4 -4
- package/dist/es/index.d.ts.map +1 -1
- package/dist/es/index.js +7 -4
- package/dist/es/ir/coordinate.d.ts +57 -0
- package/dist/es/ir/coordinate.d.ts.map +1 -0
- package/dist/es/ir/coordinate.js +27 -0
- package/dist/es/ir/index.d.ts +1 -0
- package/dist/es/ir/index.d.ts.map +1 -1
- package/dist/es/ir/node.d.ts +276 -17
- package/dist/es/ir/node.d.ts.map +1 -1
- package/dist/es/ir/node.js +28 -3
- package/dist/es/ir/path/path.d.ts +625 -15
- package/dist/es/ir/path/path.d.ts.map +1 -1
- package/dist/es/ir/path/path.js +22 -0
- package/dist/es/ir/path/step.d.ts +872 -20
- package/dist/es/ir/path/step.d.ts.map +1 -1
- package/dist/es/ir/path/step.js +86 -4
- package/dist/es/ir/path/target.d.ts +32 -2
- package/dist/es/ir/path/target.d.ts.map +1 -1
- package/dist/es/ir/path/target.js +7 -3
- package/dist/es/ir/position/at-position.d.ts +50 -0
- package/dist/es/ir/position/at-position.d.ts.map +1 -0
- package/dist/es/ir/position/at-position.js +30 -0
- package/dist/es/ir/position/index.d.ts +1 -0
- package/dist/es/ir/position/index.d.ts.map +1 -1
- package/dist/es/ir/scene.d.ts +2072 -112
- package/dist/es/ir/scene.d.ts.map +1 -1
- package/dist/es/ir/scene.js +6 -1
- package/dist/es/parsers/index.d.ts +1 -0
- package/dist/es/parsers/index.d.ts.map +1 -1
- package/dist/es/parsers/parseTargetSugar.d.ts +3 -0
- package/dist/es/parsers/parseTargetSugar.d.ts.map +1 -0
- package/dist/es/parsers/parseTargetSugar.js +31 -0
- package/dist/es/parsers/parseWay.d.ts +131 -22
- package/dist/es/parsers/parseWay.d.ts.map +1 -1
- package/dist/es/parsers/parseWay.js +149 -17
- package/dist/lib/compile/compile.cjs +33 -4
- package/dist/lib/compile/compile.d.ts +6 -0
- package/dist/lib/compile/compile.d.ts.map +1 -1
- package/dist/lib/compile/node.cjs +94 -4
- package/dist/lib/compile/node.d.ts +23 -2
- package/dist/lib/compile/node.d.ts.map +1 -1
- package/dist/lib/compile/path.cjs +333 -14
- package/dist/lib/compile/path.d.ts +3 -2
- package/dist/lib/compile/path.d.ts.map +1 -1
- package/dist/lib/compile/position.cjs +32 -5
- package/dist/lib/compile/position.d.ts +8 -5
- package/dist/lib/compile/position.d.ts.map +1 -1
- package/dist/lib/geometry/arc.cjs +55 -0
- package/dist/lib/geometry/arc.d.ts +34 -0
- package/dist/lib/geometry/arc.d.ts.map +1 -0
- package/dist/lib/geometry/bend.cjs +29 -0
- package/dist/lib/geometry/bend.d.ts +18 -0
- package/dist/lib/geometry/bend.d.ts.map +1 -0
- package/dist/lib/geometry/index.d.ts +3 -0
- package/dist/lib/geometry/index.d.ts.map +1 -1
- package/dist/lib/geometry/segment.cjs +88 -0
- package/dist/lib/geometry/segment.d.ts +38 -0
- package/dist/lib/geometry/segment.d.ts.map +1 -0
- package/dist/lib/index.cjs +18 -0
- package/dist/lib/index.d.ts +4 -4
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/ir/coordinate.cjs +27 -0
- package/dist/lib/ir/coordinate.d.ts +57 -0
- package/dist/lib/ir/coordinate.d.ts.map +1 -0
- package/dist/lib/ir/index.d.ts +1 -0
- package/dist/lib/ir/index.d.ts.map +1 -1
- package/dist/lib/ir/node.cjs +28 -2
- package/dist/lib/ir/node.d.ts +276 -17
- package/dist/lib/ir/node.d.ts.map +1 -1
- package/dist/lib/ir/path/path.cjs +22 -0
- package/dist/lib/ir/path/path.d.ts +625 -15
- package/dist/lib/ir/path/path.d.ts.map +1 -1
- package/dist/lib/ir/path/step.cjs +93 -3
- package/dist/lib/ir/path/step.d.ts +872 -20
- package/dist/lib/ir/path/step.d.ts.map +1 -1
- package/dist/lib/ir/path/target.cjs +8 -2
- package/dist/lib/ir/path/target.d.ts +32 -2
- package/dist/lib/ir/path/target.d.ts.map +1 -1
- package/dist/lib/ir/position/at-position.cjs +31 -0
- package/dist/lib/ir/position/at-position.d.ts +50 -0
- package/dist/lib/ir/position/at-position.d.ts.map +1 -0
- package/dist/lib/ir/position/index.d.ts +1 -0
- package/dist/lib/ir/position/index.d.ts.map +1 -1
- package/dist/lib/ir/scene.cjs +6 -1
- package/dist/lib/ir/scene.d.ts +2072 -112
- package/dist/lib/ir/scene.d.ts.map +1 -1
- package/dist/lib/parsers/index.d.ts +1 -0
- package/dist/lib/parsers/index.d.ts.map +1 -1
- package/dist/lib/parsers/parseTargetSugar.cjs +31 -0
- package/dist/lib/parsers/parseTargetSugar.d.ts +3 -0
- package/dist/lib/parsers/parseTargetSugar.d.ts.map +1 -0
- package/dist/lib/parsers/parseWay.cjs +149 -17
- package/dist/lib/parsers/parseWay.d.ts +131 -22
- package/dist/lib/parsers/parseWay.d.ts.map +1 -1
- 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;
|
|
1
|
+
{"version":3,"file":"scene.d.ts","sourceRoot":"","sources":["../../../src/ir/scene.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAIrB,CAAC;AAEJ,6CAA6C;AAC7C,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"}
|
package/dist/es/ir/scene.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { PathSchema } from "./path/path.js";
|
|
2
2
|
import { NodeSchema } from "./node.js";
|
|
3
|
+
import { CoordinateSchema } from "./coordinate.js";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
//#region src/ir/scene.ts
|
|
5
|
-
var ChildSchema = z.discriminatedUnion("type", [
|
|
6
|
+
var ChildSchema = z.discriminatedUnion("type", [
|
|
7
|
+
NodeSchema,
|
|
8
|
+
PathSchema,
|
|
9
|
+
CoordinateSchema
|
|
10
|
+
]).describe("Top-level scene child: a node, a path, or a coordinate placeholder; discriminator field is `type`");
|
|
6
11
|
var SceneSchema = z.object({
|
|
7
12
|
version: z.literal(1).describe("IR major version number; bump only on breaking schema changes"),
|
|
8
13
|
type: z.literal("scene").describe("Discriminator marking this object as the root scene"),
|
|
@@ -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 @@
|
|
|
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.
|
|
5
|
+
* - `DrawWay.Cycle`:闭合到 way 起点(way 元素位置)。**底层字符串故意取得很丑**
|
|
6
6
|
* (`'retikz-keyword_cycle'`),保证不会与任何合理的节点 id 冲突;用户应当
|
|
7
|
-
* 只通过 `DrawWay.
|
|
8
|
-
* - `DrawWay.
|
|
9
|
-
* - `DrawWay.
|
|
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
|
|
28
|
+
readonly Cycle: "retikz-keyword_cycle";
|
|
25
29
|
/** 折角:先水平后垂直(TikZ `-|`) */
|
|
26
|
-
readonly
|
|
30
|
+
readonly Hv: "-|";
|
|
27
31
|
/** 折角:先垂直后水平(TikZ `|-`) */
|
|
28
|
-
readonly
|
|
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
|
-
*
|
|
75
|
+
* 三次贝塞尔算子(infix):把"上一项 → 下一项"那段改成 cubic step。
|
|
76
|
+
* `cubic` 字段携带两个控制点 `[c1, c2]`。
|
|
32
77
|
*/
|
|
33
|
-
export type
|
|
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
|
-
*
|
|
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
|
|
133
|
+
export type WayLabelOp = {
|
|
134
|
+
label: WayLabel;
|
|
135
|
+
};
|
|
38
136
|
/**
|
|
39
137
|
* Sugar 层的 way 数组 DSL 元素。
|
|
40
138
|
*
|
|
41
|
-
*
|
|
139
|
+
* 接受十二种形态:
|
|
42
140
|
* - 节点 id 字符串:`'A'` → line(首项时为 move)
|
|
43
141
|
* - 笛卡尔坐标:`[x, y]` → line
|
|
44
142
|
* - 极坐标:`{ origin?, angle, radius }` → line
|
|
45
|
-
* -
|
|
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.
|
|
48
|
-
*
|
|
49
|
-
*
|
|
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.
|
|
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.
|
|
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,
|
|
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.
|
|
6
|
+
* - `DrawWay.Cycle`:闭合到 way 起点(way 元素位置)。**底层字符串故意取得很丑**
|
|
6
7
|
* (`'retikz-keyword_cycle'`),保证不会与任何合理的节点 id 冲突;用户应当
|
|
7
|
-
* 只通过 `DrawWay.
|
|
8
|
-
* - `DrawWay.
|
|
9
|
-
* - `DrawWay.
|
|
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
|
-
|
|
29
|
+
Cycle: "retikz-keyword_cycle",
|
|
25
30
|
/** 折角:先水平后垂直(TikZ `-|`) */
|
|
26
|
-
|
|
31
|
+
Hv: "-|",
|
|
27
32
|
/** 折角:先垂直后水平(TikZ `|-`) */
|
|
28
|
-
|
|
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 (
|
|
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.
|
|
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:
|
|
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 (
|
|
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
|
|
@@ -1,11 +1,35 @@
|
|
|
1
1
|
const require_rect = require("../geometry/rect.cjs");
|
|
2
|
+
const require_position = require("./position.cjs");
|
|
2
3
|
const require_node = require("./node.cjs");
|
|
4
|
+
const require_text_metrics = require("./text-metrics.cjs");
|
|
3
5
|
const require_path = require("./path.cjs");
|
|
4
6
|
const require_precision = require("./precision.cjs");
|
|
5
|
-
const require_text_metrics = require("./text-metrics.cjs");
|
|
6
7
|
const require_view_box = require("./view-box.cjs");
|
|
7
8
|
//#region src/compile/compile.ts
|
|
8
9
|
/**
|
|
10
|
+
* 把 coordinate 注册成最小 NodeLayout——0×0 rectangle,用于
|
|
11
|
+
* 后续 path target / `at.of` 引用时 boundaryPoint 命中(0×0 rect
|
|
12
|
+
* 的 boundaryPoint 始终返回中心,符合"占位无形状边界"语义)。
|
|
13
|
+
*/
|
|
14
|
+
var coordinateAsLayout = (id, center) => ({
|
|
15
|
+
id,
|
|
16
|
+
shape: "rectangle",
|
|
17
|
+
rect: {
|
|
18
|
+
x: center[0],
|
|
19
|
+
y: center[1],
|
|
20
|
+
width: 0,
|
|
21
|
+
height: 0,
|
|
22
|
+
rotate: 0
|
|
23
|
+
},
|
|
24
|
+
rotateDeg: 0,
|
|
25
|
+
margin: 0,
|
|
26
|
+
textWidth: 0,
|
|
27
|
+
textHeight: 0,
|
|
28
|
+
align: "middle",
|
|
29
|
+
lineHeight: 0,
|
|
30
|
+
fontSize: 0
|
|
31
|
+
});
|
|
32
|
+
/**
|
|
9
33
|
* IR → Scene。纯函数。
|
|
10
34
|
* 这是所有 adapter 共享的最深层共享代码。
|
|
11
35
|
*
|
|
@@ -18,19 +42,24 @@ var compileToScene = (ir, options = {}) => {
|
|
|
18
42
|
const measureText = options.measureText ?? require_text_metrics.fallbackMeasurer;
|
|
19
43
|
const viewBoxPadding = options.padding ?? 10;
|
|
20
44
|
const round = require_precision.makeRound(options.precision ?? 2);
|
|
45
|
+
const nodeDistance = options.nodeDistance;
|
|
21
46
|
const primitives = [];
|
|
22
47
|
const nodeIndex = /* @__PURE__ */ new Map();
|
|
23
48
|
const allPoints = [];
|
|
24
49
|
for (const child of ir.children) if (child.type === "node") {
|
|
25
|
-
const layout = require_node.layoutNode(child, measureText, nodeIndex);
|
|
50
|
+
const layout = require_node.layoutNode(child, measureText, nodeIndex, nodeDistance);
|
|
26
51
|
if (child.id) nodeIndex.set(child.id, layout);
|
|
27
52
|
for (const prim of require_node.emitNodePrimitives(layout, round)) primitives.push(prim);
|
|
28
53
|
allPoints.push(require_rect.rect.anchor(layout.rect, "north-west"), require_rect.rect.anchor(layout.rect, "north-east"), require_rect.rect.anchor(layout.rect, "south-west"), require_rect.rect.anchor(layout.rect, "south-east"));
|
|
54
|
+
} else if (child.type === "coordinate") {
|
|
55
|
+
const center = require_position.resolvePosition(child.position, nodeIndex, nodeDistance);
|
|
56
|
+
if (!center) throw new Error(`Cannot resolve position for coordinate ${child.id}; polar.origin or at.of may reference an undefined node`);
|
|
57
|
+
nodeIndex.set(child.id, coordinateAsLayout(child.id, center));
|
|
29
58
|
}
|
|
30
59
|
for (const child of ir.children) if (child.type === "path") {
|
|
31
|
-
const result = require_path.emitPathPrimitive(child, nodeIndex, round);
|
|
60
|
+
const result = require_path.emitPathPrimitive(child, nodeIndex, round, measureText);
|
|
32
61
|
if (result) {
|
|
33
|
-
primitives.push(
|
|
62
|
+
for (const prim of result.primitives) primitives.push(prim);
|
|
34
63
|
for (const p of result.points) allPoints.push(p);
|
|
35
64
|
}
|
|
36
65
|
}
|
|
@@ -13,6 +13,12 @@ export type CompileOptions = {
|
|
|
13
13
|
* 内部几何计算保持完整 double 精度,避免误差累积。
|
|
14
14
|
*/
|
|
15
15
|
precision?: number;
|
|
16
|
+
/**
|
|
17
|
+
* 相对定位(AtPosition)的默认距离,对应 TikZ `node distance`;user units。
|
|
18
|
+
* 当 `Node.position` 是 `{ direction, of }` 形态且未自带 `distance` 时取此值;
|
|
19
|
+
* 未配则回退到 1。
|
|
20
|
+
*/
|
|
21
|
+
nodeDistance?: number;
|
|
16
22
|
};
|
|
17
23
|
/**
|
|
18
24
|
* IR → Scene。纯函数。
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../../src/compile/compile.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAc,MAAM,OAAO,CAAC;AAC5C,OAAO,KAAK,EAAE,KAAK,EAAkB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../../src/compile/compile.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAc,MAAM,OAAO,CAAC;AAC5C,OAAO,KAAK,EAAE,KAAK,EAAkB,MAAM,cAAc,CAAC;AAK1D,OAAO,EAAE,KAAK,YAAY,EAAoB,MAAM,gBAAgB,CAAC;AAwBrE,2BAA2B;AAC3B,MAAM,MAAM,cAAc,GAAG;IAC3B,oCAAoC;IACpC,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GAAI,IAAI,EAAE,EAAE,UAAS,cAAmB,KAAG,KAsDrE,CAAC"}
|