@retikz/core 0.1.0-alpha.5 → 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.
- package/dist/es/compile/compile.d.ts +17 -0
- package/dist/es/compile/compile.d.ts.map +1 -1
- package/dist/es/compile/compile.js +40 -15
- package/dist/es/compile/index.d.ts +1 -1
- package/dist/es/compile/index.d.ts.map +1 -1
- package/dist/es/compile/node.d.ts +3 -3
- package/dist/es/compile/node.d.ts.map +1 -1
- package/dist/es/compile/node.js +1 -1
- package/dist/es/compile/path/anchor.d.ts +19 -0
- package/dist/es/compile/path/anchor.d.ts.map +1 -0
- package/dist/es/compile/path/anchor.js +54 -0
- package/dist/es/compile/{path.d.ts → path/index.d.ts} +18 -7
- package/dist/es/compile/path/index.d.ts.map +1 -0
- package/dist/es/compile/path/index.js +308 -0
- package/dist/es/compile/path/label.d.ts +18 -0
- package/dist/es/compile/path/label.d.ts.map +1 -0
- package/dist/es/compile/path/label.js +102 -0
- package/dist/es/compile/path/relative.d.ts +8 -0
- package/dist/es/compile/path/relative.d.ts.map +1 -0
- package/dist/es/compile/path/relative.js +48 -0
- package/dist/es/compile/path/shrink.d.ts +24 -0
- package/dist/es/compile/path/shrink.d.ts.map +1 -0
- package/dist/es/compile/path/shrink.js +136 -0
- package/dist/es/compile/path/split.d.ts +15 -0
- package/dist/es/compile/path/split.d.ts.map +1 -0
- package/dist/es/compile/path/split.js +46 -0
- package/dist/es/compile/text-metrics.d.ts +4 -1
- package/dist/es/compile/text-metrics.d.ts.map +1 -1
- package/dist/es/compile/text-metrics.js +11 -5
- package/dist/es/geometry/_transform.d.ts +21 -0
- package/dist/es/geometry/_transform.d.ts.map +1 -0
- package/dist/es/geometry/_transform.js +27 -0
- package/dist/es/geometry/bend.d.ts +1 -1
- package/dist/es/geometry/bend.js +1 -1
- package/dist/es/geometry/circle.d.ts +2 -3
- package/dist/es/geometry/circle.d.ts.map +1 -1
- package/dist/es/geometry/circle.js +1 -16
- package/dist/es/geometry/diamond.d.ts +2 -3
- package/dist/es/geometry/diamond.d.ts.map +1 -1
- package/dist/es/geometry/diamond.js +1 -16
- package/dist/es/geometry/ellipse.d.ts +2 -3
- package/dist/es/geometry/ellipse.d.ts.map +1 -1
- package/dist/es/geometry/ellipse.js +1 -16
- package/dist/es/geometry/polar.d.ts +2 -2
- package/dist/es/geometry/polar.d.ts.map +1 -1
- package/dist/es/geometry/polar.js +2 -6
- package/dist/es/geometry/rect.d.ts.map +1 -1
- package/dist/es/geometry/rect.js +1 -18
- package/dist/es/geometry/segment.d.ts +5 -1
- package/dist/es/geometry/segment.d.ts.map +1 -1
- package/dist/es/index.d.ts +10 -6
- package/dist/es/index.d.ts.map +1 -1
- package/dist/es/index.js +4 -2
- package/dist/es/ir/font.d.ts +21 -0
- package/dist/es/ir/font.d.ts.map +1 -0
- package/dist/es/ir/font.js +15 -0
- package/dist/es/ir/index.d.ts +2 -0
- package/dist/es/ir/index.d.ts.map +1 -1
- package/dist/es/ir/node.d.ts +28 -139
- package/dist/es/ir/node.d.ts.map +1 -1
- package/dist/es/ir/node.js +6 -30
- package/dist/es/ir/path/arrow.d.ts +12 -12
- package/dist/es/ir/path/path.d.ts +16 -16
- package/dist/es/ir/path/path.js +4 -4
- package/dist/es/ir/path/step.d.ts +1 -1
- package/dist/es/ir/path/step.d.ts.map +1 -1
- package/dist/es/ir/path/step.js +8 -8
- package/dist/es/ir/position/offset-position.js +1 -1
- package/dist/es/ir/position/polar-position.d.ts.map +1 -1
- package/dist/es/ir/position/polar-position.js +2 -2
- package/dist/es/ir/position/position.d.ts.map +1 -1
- package/dist/es/ir/position/position.js +1 -1
- package/dist/es/ir/scene.d.ts +112 -112
- package/dist/es/ir/text.d.ts +96 -0
- package/dist/es/ir/text.d.ts.map +1 -0
- package/dist/es/ir/text.js +20 -0
- package/dist/es/parsers/parseTargetSugar.d.ts +4 -0
- package/dist/es/parsers/parseTargetSugar.d.ts.map +1 -1
- package/dist/es/parsers/parseTargetSugar.js +3 -2
- package/dist/es/primitive/ellipse.d.ts +2 -2
- package/dist/es/primitive/group.d.ts +23 -7
- package/dist/es/primitive/group.d.ts.map +1 -1
- package/dist/es/primitive/path.d.ts +51 -11
- package/dist/es/primitive/path.d.ts.map +1 -1
- package/dist/es/primitive/rect.d.ts +2 -2
- package/dist/es/primitive/rect.d.ts.map +1 -1
- package/dist/es/primitive/text.d.ts +10 -3
- package/dist/es/primitive/text.d.ts.map +1 -1
- package/dist/es/primitive/view-box.d.ts +1 -1
- package/dist/es/primitive/view-box.d.ts.map +1 -1
- package/dist/es/types.d.ts +8 -0
- package/dist/es/types.d.ts.map +1 -1
- package/dist/lib/compile/compile.cjs +40 -15
- package/dist/lib/compile/compile.d.ts +17 -0
- package/dist/lib/compile/compile.d.ts.map +1 -1
- package/dist/lib/compile/index.d.ts +1 -1
- package/dist/lib/compile/index.d.ts.map +1 -1
- package/dist/lib/compile/node.cjs +1 -1
- package/dist/lib/compile/node.d.ts +3 -3
- package/dist/lib/compile/node.d.ts.map +1 -1
- package/dist/lib/compile/path/anchor.cjs +58 -0
- package/dist/lib/compile/path/anchor.d.ts +19 -0
- package/dist/lib/compile/path/anchor.d.ts.map +1 -0
- package/dist/lib/compile/path/index.cjs +308 -0
- package/dist/lib/compile/{path.d.ts → path/index.d.ts} +18 -7
- package/dist/lib/compile/path/index.d.ts.map +1 -0
- package/dist/lib/compile/path/label.cjs +103 -0
- package/dist/lib/compile/path/label.d.ts +18 -0
- package/dist/lib/compile/path/label.d.ts.map +1 -0
- package/dist/lib/compile/path/relative.cjs +48 -0
- package/dist/lib/compile/path/relative.d.ts +8 -0
- package/dist/lib/compile/path/relative.d.ts.map +1 -0
- package/dist/lib/compile/path/shrink.cjs +138 -0
- package/dist/lib/compile/path/shrink.d.ts +24 -0
- package/dist/lib/compile/path/shrink.d.ts.map +1 -0
- package/dist/lib/compile/path/split.cjs +46 -0
- package/dist/lib/compile/path/split.d.ts +15 -0
- package/dist/lib/compile/path/split.d.ts.map +1 -0
- package/dist/lib/compile/text-metrics.cjs +11 -5
- package/dist/lib/compile/text-metrics.d.ts +4 -1
- package/dist/lib/compile/text-metrics.d.ts.map +1 -1
- package/dist/lib/geometry/_transform.cjs +28 -0
- package/dist/lib/geometry/_transform.d.ts +21 -0
- package/dist/lib/geometry/_transform.d.ts.map +1 -0
- package/dist/lib/geometry/bend.cjs +1 -1
- package/dist/lib/geometry/bend.d.ts +1 -1
- package/dist/lib/geometry/circle.cjs +5 -20
- package/dist/lib/geometry/circle.d.ts +2 -3
- package/dist/lib/geometry/circle.d.ts.map +1 -1
- package/dist/lib/geometry/diamond.cjs +5 -20
- package/dist/lib/geometry/diamond.d.ts +2 -3
- package/dist/lib/geometry/diamond.d.ts.map +1 -1
- package/dist/lib/geometry/ellipse.cjs +5 -20
- package/dist/lib/geometry/ellipse.d.ts +2 -3
- package/dist/lib/geometry/ellipse.d.ts.map +1 -1
- package/dist/lib/geometry/polar.cjs +2 -6
- package/dist/lib/geometry/polar.d.ts +2 -2
- package/dist/lib/geometry/polar.d.ts.map +1 -1
- package/dist/lib/geometry/rect.cjs +5 -22
- package/dist/lib/geometry/rect.d.ts.map +1 -1
- package/dist/lib/geometry/segment.d.ts +5 -1
- package/dist/lib/geometry/segment.d.ts.map +1 -1
- package/dist/lib/index.cjs +5 -3
- package/dist/lib/index.d.ts +10 -6
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/ir/font.cjs +15 -0
- package/dist/lib/ir/font.d.ts +21 -0
- package/dist/lib/ir/font.d.ts.map +1 -0
- package/dist/lib/ir/index.d.ts +2 -0
- package/dist/lib/ir/index.d.ts.map +1 -1
- package/dist/lib/ir/node.cjs +7 -34
- package/dist/lib/ir/node.d.ts +28 -139
- package/dist/lib/ir/node.d.ts.map +1 -1
- package/dist/lib/ir/path/arrow.d.ts +12 -12
- package/dist/lib/ir/path/path.cjs +4 -4
- package/dist/lib/ir/path/path.d.ts +16 -16
- package/dist/lib/ir/path/step.cjs +8 -8
- package/dist/lib/ir/path/step.d.ts +1 -1
- package/dist/lib/ir/path/step.d.ts.map +1 -1
- package/dist/lib/ir/position/offset-position.cjs +1 -1
- package/dist/lib/ir/position/polar-position.cjs +2 -2
- package/dist/lib/ir/position/polar-position.d.ts.map +1 -1
- package/dist/lib/ir/position/position.cjs +1 -1
- package/dist/lib/ir/position/position.d.ts.map +1 -1
- package/dist/lib/ir/scene.d.ts +112 -112
- package/dist/lib/ir/text.cjs +21 -0
- package/dist/lib/ir/text.d.ts +96 -0
- package/dist/lib/ir/text.d.ts.map +1 -0
- package/dist/lib/parsers/parseTargetSugar.cjs +3 -2
- package/dist/lib/parsers/parseTargetSugar.d.ts +4 -0
- package/dist/lib/parsers/parseTargetSugar.d.ts.map +1 -1
- package/dist/lib/primitive/ellipse.d.ts +2 -2
- package/dist/lib/primitive/group.d.ts +23 -7
- package/dist/lib/primitive/group.d.ts.map +1 -1
- package/dist/lib/primitive/path.d.ts +51 -11
- package/dist/lib/primitive/path.d.ts.map +1 -1
- package/dist/lib/primitive/rect.d.ts +2 -2
- package/dist/lib/primitive/rect.d.ts.map +1 -1
- package/dist/lib/primitive/text.d.ts +10 -3
- package/dist/lib/primitive/text.d.ts.map +1 -1
- package/dist/lib/primitive/view-box.d.ts +1 -1
- package/dist/lib/primitive/view-box.d.ts.map +1 -1
- package/dist/lib/types.d.ts +8 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/package.json +13 -4
- package/dist/es/compile/path.d.ts.map +0 -1
- package/dist/es/compile/path.js +0 -636
- package/dist/lib/compile/path.cjs +0 -636
- package/dist/lib/compile/path.d.ts.map +0 -1
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { IR } from '../ir';
|
|
2
2
|
import { Scene } from '../primitive';
|
|
3
3
|
import { TextMeasurer } from './text-metrics';
|
|
4
|
+
/** 编译期警告:path / position 解析失败时通过 `CompileOptions.onWarn` 发出,不影响编译产物 */
|
|
5
|
+
export type CompileWarning = {
|
|
6
|
+
/**
|
|
7
|
+
* 警告类型代码(机器可读)
|
|
8
|
+
* @description 用户可按 code 分支处理;未来 alpha 加新 code 不破坏调用方
|
|
9
|
+
*/
|
|
10
|
+
code: 'UNRESOLVED_NODE_REFERENCE' | 'PATH_TOO_SHORT' | 'ANCHOR_RESOLUTION_FAILED' | 'OFFSET_BASE_UNRESOLVED' | 'POLAR_ORIGIN_UNRESOLVED' | 'AT_TARGET_UNRESOLVED' | 'RELATIVE_INITIAL_NO_PREV_END' | 'BBOX_EXTREME_INPUT' | (string & {});
|
|
11
|
+
/** 人类可读消息(英文) */
|
|
12
|
+
message: string;
|
|
13
|
+
/** IR locator 路径(jq-like,如 `'children[3].path.children[1].to'`) */
|
|
14
|
+
path: string;
|
|
15
|
+
};
|
|
4
16
|
/** compileToScene 的可选参数 */
|
|
5
17
|
export type CompileOptions = {
|
|
6
18
|
/** 注入文字度量函数;不传则用 fallback(不准但可跑) */
|
|
@@ -17,6 +29,11 @@ export type CompileOptions = {
|
|
|
17
29
|
* @description `Node.position` 为 `{ direction, of }` 且未自带 `distance` 时取此值;未配回退到 1
|
|
18
30
|
*/
|
|
19
31
|
nodeDistance?: number;
|
|
32
|
+
/**
|
|
33
|
+
* 编译期警告收集器
|
|
34
|
+
* @description path / position 解析失败时按 IR locator + code + message 同步触发;不传时 dev 模式(`process.env.NODE_ENV !== 'production'`)默认 `console.warn`、生产静默
|
|
35
|
+
*/
|
|
36
|
+
onWarn?: (warning: CompileWarning) => void;
|
|
20
37
|
};
|
|
21
38
|
/**
|
|
22
39
|
* IR → Scene 纯函数转换,所有 adapter 共享
|
|
@@ -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;AAK1D,OAAO,EAAE,KAAK,YAAY,EAAoB,MAAM,gBAAgB,CAAC;AAuBrE,2BAA2B;AAC3B,MAAM,MAAM,cAAc,GAAG;IAC3B,oCAAoC;IACpC,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,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;AAuBrE,uEAAuE;AACvE,MAAM,MAAM,cAAc,GAAG;IAC3B;;;OAGG;IACH,IAAI,EACA,2BAA2B,GAC3B,gBAAgB,GAChB,0BAA0B,GAC1B,wBAAwB,GACxB,yBAAyB,GACzB,sBAAsB,GACtB,8BAA8B,GAC9B,oBAAoB,GACpB,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAClB,iBAAiB;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,2BAA2B;AAC3B,MAAM,MAAM,cAAc,GAAG;IAC3B,oCAAoC;IACpC,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;CAC5C,CAAC;AAWF;;;GAGG;AACH,eAAO,MAAM,cAAc,GAAI,IAAI,EAAE,EAAE,UAAS,cAAmB,KAAG,KAkErE,CAAC"}
|
|
@@ -2,7 +2,7 @@ import { rect } from "../geometry/rect.js";
|
|
|
2
2
|
import { resolvePosition } from "./position.js";
|
|
3
3
|
import { emitNodePrimitives, layoutNode } from "./node.js";
|
|
4
4
|
import { fallbackMeasurer } from "./text-metrics.js";
|
|
5
|
-
import { emitPathPrimitive } from "./path.js";
|
|
5
|
+
import { emitPathPrimitive } from "./path/index.js";
|
|
6
6
|
import { makeRound } from "./precision.js";
|
|
7
7
|
import { computeViewBox } from "./view-box.js";
|
|
8
8
|
//#region src/compile/compile.ts
|
|
@@ -29,6 +29,14 @@ var coordinateAsLayout = (id, center) => ({
|
|
|
29
29
|
fontSize: 0
|
|
30
30
|
});
|
|
31
31
|
/**
|
|
32
|
+
* 默认 warn dispatcher:dev 模式 console.warn、生产静默
|
|
33
|
+
* @description 用户传 onWarn 时使用用户的;不传走此 fallback
|
|
34
|
+
*/
|
|
35
|
+
var defaultWarnDispatcher = (warning) => {
|
|
36
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV === "production") return;
|
|
37
|
+
console.warn(`[retikz] ${warning.code} at ${warning.path}: ${warning.message}`);
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
32
40
|
* IR → Scene 纯函数转换,所有 adapter 共享
|
|
33
41
|
* @description Pass 1 处理 Node/coordinate 并注册 nodeIndex、发 primitive、累积 bbox;Pass 2 解析 Path 端点写 d 字符串;末端按 precision 折算 viewBox
|
|
34
42
|
*/
|
|
@@ -37,24 +45,41 @@ var compileToScene = (ir, options = {}) => {
|
|
|
37
45
|
const viewBoxPadding = options.padding ?? 10;
|
|
38
46
|
const round = makeRound(options.precision ?? 2);
|
|
39
47
|
const nodeDistance = options.nodeDistance;
|
|
48
|
+
const onWarn = options.onWarn ?? defaultWarnDispatcher;
|
|
40
49
|
const primitives = [];
|
|
41
50
|
const nodeIndex = /* @__PURE__ */ new Map();
|
|
42
51
|
const allPoints = [];
|
|
43
|
-
for (
|
|
44
|
-
const
|
|
45
|
-
if (child.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
for (let i = 0; i < ir.children.length; i++) {
|
|
53
|
+
const child = ir.children[i];
|
|
54
|
+
if (child.type === "node") {
|
|
55
|
+
const layout = layoutNode(child, measureText, nodeIndex, nodeDistance);
|
|
56
|
+
if (child.id) nodeIndex.set(child.id, layout);
|
|
57
|
+
for (const prim of emitNodePrimitives(layout, round)) primitives.push(prim);
|
|
58
|
+
allPoints.push(rect.anchor(layout.rect, "north-west"), rect.anchor(layout.rect, "north-east"), rect.anchor(layout.rect, "south-west"), rect.anchor(layout.rect, "south-east"));
|
|
59
|
+
} else if (child.type === "coordinate") {
|
|
60
|
+
const center = resolvePosition(child.position, nodeIndex, nodeDistance);
|
|
61
|
+
if (!center) {
|
|
62
|
+
onWarn({
|
|
63
|
+
code: "POLAR_ORIGIN_UNRESOLVED",
|
|
64
|
+
message: `Cannot resolve position for coordinate '${child.id}'; polar.origin or at.of may reference an undefined node`,
|
|
65
|
+
path: `children[${i}].coordinate.position`
|
|
66
|
+
});
|
|
67
|
+
throw new Error(`Cannot resolve position for coordinate ${child.id}; polar.origin or at.of may reference an undefined node`);
|
|
68
|
+
}
|
|
69
|
+
nodeIndex.set(child.id, coordinateAsLayout(child.id, center));
|
|
70
|
+
}
|
|
52
71
|
}
|
|
53
|
-
for (
|
|
54
|
-
const
|
|
55
|
-
if (
|
|
56
|
-
|
|
57
|
-
|
|
72
|
+
for (let i = 0; i < ir.children.length; i++) {
|
|
73
|
+
const child = ir.children[i];
|
|
74
|
+
if (child.type === "path") {
|
|
75
|
+
const result = emitPathPrimitive(child, nodeIndex, round, measureText, {
|
|
76
|
+
onWarn,
|
|
77
|
+
irPath: `children[${i}].path`
|
|
78
|
+
});
|
|
79
|
+
if (result) {
|
|
80
|
+
for (const prim of result.primitives) primitives.push(prim);
|
|
81
|
+
for (const p of result.points) allPoints.push(p);
|
|
82
|
+
}
|
|
58
83
|
}
|
|
59
84
|
}
|
|
60
85
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/compile/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,eAAe,CAAC;AAC9B,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/compile/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC"}
|
|
@@ -13,7 +13,7 @@ export type NodeLayout = {
|
|
|
13
13
|
* @description rectangle: rect 本身;circle: width=height=2×radius;ellipse: 2×rx,2×ry;diamond: 2×halfA,2×halfB。x,y 是几何中心,rotate 弧度
|
|
14
14
|
*/
|
|
15
15
|
rect: Rect;
|
|
16
|
-
/** IR 原始旋转角(度数),供 emit
|
|
16
|
+
/** IR 原始旋转角(度数),供 emit 阶段写入 GroupPrim 的 rotate transform */
|
|
17
17
|
rotateDeg: number;
|
|
18
18
|
/** 外边距(≥ 0),path 附着到外扩 margin 的虚拟边界 */
|
|
19
19
|
margin: number;
|
|
@@ -26,7 +26,7 @@ export type NodeLayout = {
|
|
|
26
26
|
textWidth: number;
|
|
27
27
|
/** 文本块高度 ≈ lines × lineHeight */
|
|
28
28
|
textHeight: number;
|
|
29
|
-
/**
|
|
29
|
+
/** 文本对齐(start / middle / end 三态) */
|
|
30
30
|
align: 'start' | 'middle' | 'end';
|
|
31
31
|
/** 行高(已应用默认值) */
|
|
32
32
|
lineHeight: number;
|
|
@@ -48,7 +48,7 @@ export type NodeLayout = {
|
|
|
48
48
|
strokeOpacity?: number;
|
|
49
49
|
/** 边框宽度,emit 时 1 兜底 */
|
|
50
50
|
strokeWidth?: number;
|
|
51
|
-
/** SVG stroke-dasharray
|
|
51
|
+
/** 描边 dash 模式(与 SVG/CSS `stroke-dasharray` 同格式),已把 dashed/dotted 预设解析为具体 pattern */
|
|
52
52
|
strokeDasharray?: string;
|
|
53
53
|
/** rectangle 圆角半径(非 rect shape 无效) */
|
|
54
54
|
roundedCorners?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../src/compile/node.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,EAAe,SAAS,EAAE,MAAM,OAAO,CAAC;AACrF,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAgCnD,MAAM,MAAM,UAAU,GAAG;IACvB,qBAAqB;IACrB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,2CAA2C;IAC3C,KAAK,EAAE,SAAS,CAAC;IACjB;;;OAGG;IACH,IAAI,EAAE,IAAI,CAAC;IACX,
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../src/compile/node.ts"],"names":[],"mappings":"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"}
|
package/dist/es/compile/node.js
CHANGED
|
@@ -21,7 +21,7 @@ var resolveDashArray = (dashArray, dashed, dotted) => {
|
|
|
21
21
|
if (dashed) return DASHED_PATTERN;
|
|
22
22
|
if (dotted) return DOTTED_PATTERN;
|
|
23
23
|
};
|
|
24
|
-
/** IR align →
|
|
24
|
+
/** IR align → 文字对齐锚点(start / middle / end) */
|
|
25
25
|
var alignToTextAnchor = (a) => a === "left" ? "start" : a === "right" ? "end" : "middle";
|
|
26
26
|
/** 从 layout 构造 Rect(带 margin 扩张) */
|
|
27
27
|
var rectOf = (layout, marginAdd) => ({
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { IRPosition, IRTarget } from '../../ir';
|
|
2
|
+
import { NodeLayout } from '../node';
|
|
3
|
+
/**
|
|
4
|
+
* 求 step.to 的参考点(给 boundary clip 算方向 / 折角 corner 用)
|
|
5
|
+
* @description 三态:`'A'`(auto) 节点中心;`'A.<anchor>'`/`'A.<deg>'` 显式锚点 refPoint=endpoint 位置不随邻居变。直接坐标/极坐标解析为笛卡尔
|
|
6
|
+
*/
|
|
7
|
+
export declare const refPointOfTarget: (target: IRTarget, nodeIndex: Map<string, NodeLayout>) => IRPosition | null;
|
|
8
|
+
/** 折角中间点:`-|` → (curr.x, prev.y);`|-` → (prev.x, curr.y) */
|
|
9
|
+
export declare const cornerOf: (prev: IRPosition, curr: IRPosition, via: "-|" | "|-") => IRPosition;
|
|
10
|
+
/**
|
|
11
|
+
* 在 toward 方向算 step.to 的实际绘制端点
|
|
12
|
+
* @description 节点 auto `'A'`:按 shape 走 boundaryPointOf 求中心→toward 射线交点;命名 anchor/角度:位置已定不受 toward 影响;直接坐标/极坐标:解析后返回;失败返回 null
|
|
13
|
+
*/
|
|
14
|
+
export declare const clipForTarget: (target: IRTarget, toward: IRPosition, nodeIndex: Map<string, NodeLayout>) => IRPosition | null;
|
|
15
|
+
/** 两个 IRPosition 两分量精确相等(未 round) */
|
|
16
|
+
export declare const samePoint: (a: IRPosition | null, b: IRPosition | null) => boolean;
|
|
17
|
+
/** 把 p 朝 target 方向移动 dist */
|
|
18
|
+
export declare const shiftToward: (p: IRPosition, target: IRPosition, dist: number) => IRPosition;
|
|
19
|
+
//# sourceMappingURL=anchor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchor.d.ts","sourceRoot":"","sources":["../../../../src/compile/path/anchor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACrD,OAAO,EAAE,KAAK,UAAU,EAA8C,MAAM,SAAS,CAAC;AAItF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,QAAQ,EAChB,WAAW,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,KACjC,UAAU,GAAG,IAmBf,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,QAAQ,GACnB,MAAM,UAAU,EAChB,MAAM,UAAU,EAChB,KAAK,IAAI,GAAG,IAAI,KACf,UACqD,CAAC;AAEzD;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,QAAQ,QAAQ,EAChB,QAAQ,UAAU,EAClB,WAAW,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,KACjC,UAAU,GAAG,IAmBf,CAAC;AAEF,qCAAqC;AACrC,eAAO,MAAM,SAAS,GAAI,GAAG,UAAU,GAAG,IAAI,EAAE,GAAG,UAAU,GAAG,IAAI,KAAG,OACzB,CAAC;AAE/C,6BAA6B;AAC7B,eAAO,MAAM,WAAW,GAAI,GAAG,UAAU,EAAE,QAAQ,UAAU,EAAE,MAAM,MAAM,KAAG,UAM7E,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { resolvePosition } from "../position.js";
|
|
2
|
+
import { anchorOf, angleBoundaryOf, boundaryPointOf } from "../node.js";
|
|
3
|
+
import { parseNodeRef } from "../parseTarget.js";
|
|
4
|
+
//#region src/compile/path/anchor.ts
|
|
5
|
+
/**
|
|
6
|
+
* 求 step.to 的参考点(给 boundary clip 算方向 / 折角 corner 用)
|
|
7
|
+
* @description 三态:`'A'`(auto) 节点中心;`'A.<anchor>'`/`'A.<deg>'` 显式锚点 refPoint=endpoint 位置不随邻居变。直接坐标/极坐标解析为笛卡尔
|
|
8
|
+
*/
|
|
9
|
+
var refPointOfTarget = (target, nodeIndex) => {
|
|
10
|
+
if (typeof target === "string") {
|
|
11
|
+
const ref = parseNodeRef(target);
|
|
12
|
+
const node = nodeIndex.get(ref.id);
|
|
13
|
+
if (!node) return null;
|
|
14
|
+
switch (ref.kind) {
|
|
15
|
+
case "node": return [node.rect.x, node.rect.y];
|
|
16
|
+
case "anchor": return anchorOf(node, ref.anchor);
|
|
17
|
+
case "angle": return angleBoundaryOf(node, ref.angle);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (typeof target === "object" && !Array.isArray(target) && ("relative" in target || "relativeAccumulate" in target)) return null;
|
|
21
|
+
return resolvePosition(target, nodeIndex);
|
|
22
|
+
};
|
|
23
|
+
/** 折角中间点:`-|` → (curr.x, prev.y);`|-` → (prev.x, curr.y) */
|
|
24
|
+
var cornerOf = (prev, curr, via) => via === "-|" ? [curr[0], prev[1]] : [prev[0], curr[1]];
|
|
25
|
+
/**
|
|
26
|
+
* 在 toward 方向算 step.to 的实际绘制端点
|
|
27
|
+
* @description 节点 auto `'A'`:按 shape 走 boundaryPointOf 求中心→toward 射线交点;命名 anchor/角度:位置已定不受 toward 影响;直接坐标/极坐标:解析后返回;失败返回 null
|
|
28
|
+
*/
|
|
29
|
+
var clipForTarget = (target, toward, nodeIndex) => {
|
|
30
|
+
if (typeof target === "string") {
|
|
31
|
+
const ref = parseNodeRef(target);
|
|
32
|
+
const node = nodeIndex.get(ref.id);
|
|
33
|
+
if (!node) return null;
|
|
34
|
+
switch (ref.kind) {
|
|
35
|
+
case "node": return boundaryPointOf(node, toward);
|
|
36
|
+
case "anchor": return anchorOf(node, ref.anchor);
|
|
37
|
+
case "angle": return angleBoundaryOf(node, ref.angle);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (typeof target === "object" && !Array.isArray(target) && ("relative" in target || "relativeAccumulate" in target)) return null;
|
|
41
|
+
return resolvePosition(target, nodeIndex);
|
|
42
|
+
};
|
|
43
|
+
/** 两个 IRPosition 两分量精确相等(未 round) */
|
|
44
|
+
var samePoint = (a, b) => !!a && !!b && a[0] === b[0] && a[1] === b[1];
|
|
45
|
+
/** 把 p 朝 target 方向移动 dist */
|
|
46
|
+
var shiftToward = (p, target, dist) => {
|
|
47
|
+
const dx = target[0] - p[0];
|
|
48
|
+
const dy = target[1] - p[1];
|
|
49
|
+
const len = Math.sqrt(dx * dx + dy * dy);
|
|
50
|
+
if (len === 0 || dist === 0) return p;
|
|
51
|
+
return [p[0] + dx / len * dist, p[1] + dy / len * dist];
|
|
52
|
+
};
|
|
53
|
+
//#endregion
|
|
54
|
+
export { clipForTarget, cornerOf, refPointOfTarget, samePoint, shiftToward };
|
|
@@ -1,13 +1,24 @@
|
|
|
1
|
-
import { IRPath, IRPosition } from '
|
|
2
|
-
import { ScenePrimitive } from '
|
|
3
|
-
import { NodeLayout } from '
|
|
4
|
-
import { TextMeasurer } from '
|
|
1
|
+
import { IRPath, IRPosition } from '../../ir';
|
|
2
|
+
import { ScenePrimitive } from '../../primitive';
|
|
3
|
+
import { NodeLayout } from '../node';
|
|
4
|
+
import { TextMeasurer } from '../text-metrics';
|
|
5
|
+
/** emitPathPrimitive 可选 warn 钩子 */
|
|
6
|
+
export type EmitPathWarnHook = {
|
|
7
|
+
/** 警告收集器(由 compileToScene 传入) */
|
|
8
|
+
onWarn?: (warning: {
|
|
9
|
+
code: string;
|
|
10
|
+
message: string;
|
|
11
|
+
path: string;
|
|
12
|
+
}) => void;
|
|
13
|
+
/** 当前 path 在 IR 中的 locator 前缀(如 `'children[3].path'`) */
|
|
14
|
+
irPath?: string;
|
|
15
|
+
};
|
|
5
16
|
/**
|
|
6
17
|
* IR Path → PathPrim
|
|
7
|
-
* @description 每个绘制段独立用节点中心算两端 boundary clip——中段节点的入/出 boundary 点通常不同,path 在该节点可见"断开"(与 TikZ `\draw (A)--(B)--(C);` 段独立 clip 一致)。仍产一个 PathPrim:commands 用多组 move/line 表达 sub-path;段起点等于上段终点时复用 cursor 省 move。cycle 段闭回最近 move 起点,起点==lastEnd && 终点==subPathStart 时输出 close,否则显式画段 line。引用未定义节点/解析失败返回 null
|
|
18
|
+
* @description 每个绘制段独立用节点中心算两端 boundary clip——中段节点的入/出 boundary 点通常不同,path 在该节点可见"断开"(与 TikZ `\draw (A)--(B)--(C);` 段独立 clip 一致)。仍产一个 PathPrim:commands 用多组 move/line 表达 sub-path;段起点等于上段终点时复用 cursor 省 move。cycle 段闭回最近 move 起点,起点==lastEnd && 终点==subPathStart 时输出 close,否则显式画段 line。引用未定义节点/解析失败返回 null,并通过 `warnHook.onWarn` 同步触发 warning
|
|
8
19
|
*/
|
|
9
|
-
export declare const emitPathPrimitive: (path: IRPath, nodeIndex: Map<string, NodeLayout>, round: (n: number) => number, measureText?: TextMeasurer) => {
|
|
20
|
+
export declare const emitPathPrimitive: (path: IRPath, nodeIndex: Map<string, NodeLayout>, round: (n: number) => number, measureText?: TextMeasurer, warnHook?: EmitPathWarnHook) => {
|
|
10
21
|
primitives: Array<ScenePrimitive>;
|
|
11
22
|
points: Array<IRPosition>;
|
|
12
23
|
} | null;
|
|
13
|
-
//# sourceMappingURL=
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/compile/path/index.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,MAAM,EACN,UAAU,EAGX,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAEV,cAAc,EACf,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,KAAK,YAAY,EAAoB,MAAM,iBAAiB,CAAC;AA8BtE,mCAAmC;AACnC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,iCAAiC;IACjC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;KACd,KAAK,IAAI,CAAC;IACX,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAC5B,MAAM,MAAM,EACZ,WAAW,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EAClC,OAAO,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAC5B,cAAa,YAA+B,EAC5C,WAAU,gBAAqB,KAC9B;IAAE,UAAU,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;IAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAA;CAAE,GAAG,IA0XrE,CAAC"}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { arcBoundingPoints, arcEndPoint } from "../../geometry/arc.js";
|
|
2
|
+
import { bendControlPoints } from "../../geometry/bend.js";
|
|
3
|
+
import { arcSegmentSample, circleSegmentSample, cubicSegmentSample, ellipseSegmentSample, foldSegmentSample, lineSegmentSample, quadSegmentSample } from "../../geometry/segment.js";
|
|
4
|
+
import { fallbackMeasurer } from "../text-metrics.js";
|
|
5
|
+
import { clipForTarget, cornerOf, refPointOfTarget, samePoint } from "./anchor.js";
|
|
6
|
+
import { emitLabelPrimitive, tForLabelPosition } from "./label.js";
|
|
7
|
+
import { normalizeRelativeTargets } from "./relative.js";
|
|
8
|
+
import { applyArrowShrinks, arrowMarkers, computeShrink } from "./shrink.js";
|
|
9
|
+
import { splitSubPathsForMarkers } from "./split.js";
|
|
10
|
+
//#region src/compile/path/index.ts
|
|
11
|
+
/**
|
|
12
|
+
* 语义 stroke 档位 → 数值(user units)
|
|
13
|
+
* @description 对齐 TikZ 比例(thin=0.4pt→1=默认 strokeWidth):ultraThin 0.25、veryThin 0.5、thin 1、semithick 1.5、thick 2、veryThick 3、ultraThick 4。显式 strokeWidth 覆盖 thickness。
|
|
14
|
+
* `as const satisfies` + `AssertEqual` 双约束:加 IRPath['thickness'] 档位时漏写 TS 报错(字段表互锁,同 ADR-06 主题)
|
|
15
|
+
*/
|
|
16
|
+
var THICKNESS_TO_WIDTH = {
|
|
17
|
+
ultraThin: .25,
|
|
18
|
+
veryThin: .5,
|
|
19
|
+
thin: 1,
|
|
20
|
+
semithick: 1.5,
|
|
21
|
+
thick: 2,
|
|
22
|
+
veryThick: 3,
|
|
23
|
+
ultraThick: 4
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* IR Path → PathPrim
|
|
27
|
+
* @description 每个绘制段独立用节点中心算两端 boundary clip——中段节点的入/出 boundary 点通常不同,path 在该节点可见"断开"(与 TikZ `\draw (A)--(B)--(C);` 段独立 clip 一致)。仍产一个 PathPrim:commands 用多组 move/line 表达 sub-path;段起点等于上段终点时复用 cursor 省 move。cycle 段闭回最近 move 起点,起点==lastEnd && 终点==subPathStart 时输出 close,否则显式画段 line。引用未定义节点/解析失败返回 null,并通过 `warnHook.onWarn` 同步触发 warning
|
|
28
|
+
*/
|
|
29
|
+
var emitPathPrimitive = (path, nodeIndex, round, measureText = fallbackMeasurer, warnHook = {}) => {
|
|
30
|
+
const irPath = warnHook.irPath ?? "path";
|
|
31
|
+
const warn = (code, message, subPath = "") => {
|
|
32
|
+
warnHook.onWarn?.({
|
|
33
|
+
code,
|
|
34
|
+
message,
|
|
35
|
+
path: subPath ? `${irPath}.${subPath}` : irPath
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
const steps = normalizeRelativeTargets(path.children, nodeIndex);
|
|
39
|
+
if (steps.length < 2) {
|
|
40
|
+
warn("PATH_TOO_SHORT", `Path requires at least 2 steps (got ${steps.length}); the entire path is skipped`, "children");
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
/** 每段 step.label 翻译出的 TextPrim(或 sloped 旋转的 group),与 path 主体同级返回 */
|
|
44
|
+
const labelPrims = [];
|
|
45
|
+
/** 算 sample 后 emitLabelPrimitive,结果累积到 labelPrims/points */
|
|
46
|
+
const collectLabel = (step, sampleAt) => {
|
|
47
|
+
if (step.kind === "move" || step.kind === "cycle" || !("label" in step) || !step.label) return;
|
|
48
|
+
const sample = sampleAt(tForLabelPosition(step.label.position));
|
|
49
|
+
const r = emitLabelPrimitive(step.label, sample, measureText, round);
|
|
50
|
+
labelPrims.push(r.primitive);
|
|
51
|
+
for (const p of r.points) points.push(p);
|
|
52
|
+
};
|
|
53
|
+
const hasTo = (s) => s.kind !== "cycle" && s.kind !== "arc" && s.kind !== "circlePath" && s.kind !== "ellipsePath";
|
|
54
|
+
const anchors = steps.map((s, idx) => {
|
|
55
|
+
if (!hasTo(s)) return null;
|
|
56
|
+
const ref = refPointOfTarget(s.to, nodeIndex);
|
|
57
|
+
if (!ref && typeof s.to === "string") warn("UNRESOLVED_NODE_REFERENCE", `Step.to references undefined node id '${s.to}'; the entire path is skipped`, `children[${idx}].to`);
|
|
58
|
+
return ref;
|
|
59
|
+
});
|
|
60
|
+
/**
|
|
61
|
+
* 单调指针:最近一个 hasTo step 的索引;主循环开头根据上一 step 推进;O(1) 读
|
|
62
|
+
* @description 旧实现 findPrev(i) 反向扫 anchors 数组每步 O(i)、整 path O(n²);改为只在 step `i-1` 是 hasTo 时推进。anchor 失效"中毒"判断保留:lastHasToIdx 指向的 anchor 为 null 时 findPrev 返回 null,与旧"扫到第一个 hasTo 步若 anchor=null 返回 null"语义等价
|
|
63
|
+
*/
|
|
64
|
+
let lastHasToIdx = -1;
|
|
65
|
+
/** 同步维护:最近一个 move 步的 `to`,给 cycle 闭合用;旧 findRecentMoveTo 反向扫 → O(1) */
|
|
66
|
+
let lastMoveTo = null;
|
|
67
|
+
/** 找最近一个"有 to 字段的 step" 及其 anchor;O(1) 读 lastHasToIdx */
|
|
68
|
+
const findPrev = () => {
|
|
69
|
+
if (lastHasToIdx === -1) return null;
|
|
70
|
+
const s = steps[lastHasToIdx];
|
|
71
|
+
if (!hasTo(s)) return null;
|
|
72
|
+
const a = anchors[lastHasToIdx];
|
|
73
|
+
if (!a) return null;
|
|
74
|
+
return {
|
|
75
|
+
step: s,
|
|
76
|
+
anchor: a
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
const commands = [];
|
|
80
|
+
const points = [];
|
|
81
|
+
let lastEnd = null;
|
|
82
|
+
let subPathStart = null;
|
|
83
|
+
/**
|
|
84
|
+
* 笔位覆盖:arc/circlePath/ellipsePath 无 `to` 字段不能用 prev.step.to 重算起点
|
|
85
|
+
* @description 设置 penOverride 让下个绘制段直接用此点当 fromClip 后清空。arc=弧终点;circlePath/ellipsePath=center("画完留在圆心")
|
|
86
|
+
*/
|
|
87
|
+
let penOverride = null;
|
|
88
|
+
const roundPoint = (p) => [round(p[0]), round(p[1])];
|
|
89
|
+
const emitMove = (p) => {
|
|
90
|
+
const rp = roundPoint(p);
|
|
91
|
+
commands.push({
|
|
92
|
+
kind: "move",
|
|
93
|
+
to: [rp[0], rp[1]]
|
|
94
|
+
});
|
|
95
|
+
points.push(p);
|
|
96
|
+
subPathStart = p;
|
|
97
|
+
lastEnd = p;
|
|
98
|
+
};
|
|
99
|
+
const emitLine = (p) => {
|
|
100
|
+
const rp = roundPoint(p);
|
|
101
|
+
commands.push({
|
|
102
|
+
kind: "line",
|
|
103
|
+
to: [rp[0], rp[1]]
|
|
104
|
+
});
|
|
105
|
+
points.push(p);
|
|
106
|
+
lastEnd = p;
|
|
107
|
+
};
|
|
108
|
+
const emitClose = () => {
|
|
109
|
+
commands.push({ kind: "close" });
|
|
110
|
+
lastEnd = subPathStart;
|
|
111
|
+
};
|
|
112
|
+
const emitQuad = (control, p) => {
|
|
113
|
+
const rc = roundPoint(control);
|
|
114
|
+
const rp = roundPoint(p);
|
|
115
|
+
commands.push({
|
|
116
|
+
kind: "quad",
|
|
117
|
+
control: [rc[0], rc[1]],
|
|
118
|
+
to: [rp[0], rp[1]]
|
|
119
|
+
});
|
|
120
|
+
points.push(control);
|
|
121
|
+
points.push(p);
|
|
122
|
+
lastEnd = p;
|
|
123
|
+
};
|
|
124
|
+
const emitCubic = (c1, c2, p) => {
|
|
125
|
+
const rc1 = roundPoint(c1);
|
|
126
|
+
const rc2 = roundPoint(c2);
|
|
127
|
+
const rp = roundPoint(p);
|
|
128
|
+
commands.push({
|
|
129
|
+
kind: "cubic",
|
|
130
|
+
control1: [rc1[0], rc1[1]],
|
|
131
|
+
control2: [rc2[0], rc2[1]],
|
|
132
|
+
to: [rp[0], rp[1]]
|
|
133
|
+
});
|
|
134
|
+
points.push(c1);
|
|
135
|
+
points.push(c2);
|
|
136
|
+
points.push(p);
|
|
137
|
+
lastEnd = p;
|
|
138
|
+
};
|
|
139
|
+
const emitArc = (center, radius, startAngle, endAngle) => {
|
|
140
|
+
const rc = roundPoint(center);
|
|
141
|
+
commands.push({
|
|
142
|
+
kind: "arc",
|
|
143
|
+
center: [rc[0], rc[1]],
|
|
144
|
+
radius: round(radius),
|
|
145
|
+
startAngle,
|
|
146
|
+
endAngle
|
|
147
|
+
});
|
|
148
|
+
points.push(arcEndPoint(center, radius, endAngle));
|
|
149
|
+
lastEnd = arcEndPoint(center, radius, endAngle);
|
|
150
|
+
};
|
|
151
|
+
const emitEllipseArc = (center, radiusX, radiusY, startAngle, endAngle) => {
|
|
152
|
+
const rc = roundPoint(center);
|
|
153
|
+
commands.push({
|
|
154
|
+
kind: "ellipseArc",
|
|
155
|
+
center: [rc[0], rc[1]],
|
|
156
|
+
radiusX: round(radiusX),
|
|
157
|
+
radiusY: round(radiusY),
|
|
158
|
+
startAngle,
|
|
159
|
+
endAngle
|
|
160
|
+
});
|
|
161
|
+
const endPt = [center[0] + Math.cos(endAngle * Math.PI / 180) * radiusX, center[1] + Math.sin(endAngle * Math.PI / 180) * radiusY];
|
|
162
|
+
points.push(endPt);
|
|
163
|
+
lastEnd = endPt;
|
|
164
|
+
};
|
|
165
|
+
/** 段起点:与 lastEnd 相同则复用 cursor(省 move),否则发 move */
|
|
166
|
+
const startSegment = (p) => {
|
|
167
|
+
if (samePoint(p, lastEnd)) return;
|
|
168
|
+
emitMove(p);
|
|
169
|
+
};
|
|
170
|
+
for (let i = 0; i < steps.length; i++) {
|
|
171
|
+
if (i > 0) {
|
|
172
|
+
const prevStep = steps[i - 1];
|
|
173
|
+
if (hasTo(prevStep)) lastHasToIdx = i - 1;
|
|
174
|
+
if (prevStep.kind === "move") lastMoveTo = prevStep.to;
|
|
175
|
+
}
|
|
176
|
+
const step = steps[i];
|
|
177
|
+
if (step.kind === "move") continue;
|
|
178
|
+
if (step.kind === "cycle") {
|
|
179
|
+
const moveTo = lastMoveTo;
|
|
180
|
+
const prev = findPrev();
|
|
181
|
+
if (!moveTo || !prev) continue;
|
|
182
|
+
const moveAnchor = refPointOfTarget(moveTo, nodeIndex);
|
|
183
|
+
if (!moveAnchor) return null;
|
|
184
|
+
const fromClip = clipForTarget(prev.step.to, moveAnchor, nodeIndex);
|
|
185
|
+
const toClip = clipForTarget(moveTo, prev.anchor, nodeIndex);
|
|
186
|
+
if (!fromClip || !toClip) return null;
|
|
187
|
+
if (samePoint(fromClip, lastEnd) && samePoint(toClip, subPathStart)) {
|
|
188
|
+
emitClose();
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
startSegment(fromClip);
|
|
192
|
+
emitLine(toClip);
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
const prev = findPrev();
|
|
196
|
+
if (!prev) return null;
|
|
197
|
+
if (step.kind === "arc") {
|
|
198
|
+
const center = prev.anchor;
|
|
199
|
+
const startPt = arcEndPoint(center, step.radius, step.startAngle);
|
|
200
|
+
const endPt = arcEndPoint(center, step.radius, step.endAngle);
|
|
201
|
+
startSegment(startPt);
|
|
202
|
+
emitArc(center, step.radius, step.startAngle, step.endAngle);
|
|
203
|
+
for (const p of arcBoundingPoints(center, step.radius, step.startAngle, step.endAngle)) points.push(p);
|
|
204
|
+
collectLabel(step, (t) => arcSegmentSample(center, step.radius, step.startAngle, step.endAngle, t));
|
|
205
|
+
penOverride = endPt;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (step.kind === "circlePath") {
|
|
209
|
+
const center = prev.anchor;
|
|
210
|
+
const r = step.radius;
|
|
211
|
+
startSegment([center[0] + r, center[1]]);
|
|
212
|
+
emitEllipseArc(center, r, r, 0, 360);
|
|
213
|
+
points.push([center[0] + r, center[1]]);
|
|
214
|
+
points.push([center[0] - r, center[1]]);
|
|
215
|
+
points.push([center[0], center[1] + r]);
|
|
216
|
+
points.push([center[0], center[1] - r]);
|
|
217
|
+
collectLabel(step, (t) => circleSegmentSample(center, r, t));
|
|
218
|
+
penOverride = center;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
if (step.kind === "ellipsePath") {
|
|
222
|
+
const center = prev.anchor;
|
|
223
|
+
const rx = step.radiusX;
|
|
224
|
+
const ry = step.radiusY;
|
|
225
|
+
startSegment([center[0] + rx, center[1]]);
|
|
226
|
+
emitEllipseArc(center, rx, ry, 0, 360);
|
|
227
|
+
points.push([center[0] + rx, center[1]]);
|
|
228
|
+
points.push([center[0] - rx, center[1]]);
|
|
229
|
+
points.push([center[0], center[1] + ry]);
|
|
230
|
+
points.push([center[0], center[1] - ry]);
|
|
231
|
+
collectLabel(step, (t) => ellipseSegmentSample(center, rx, ry, t));
|
|
232
|
+
penOverride = center;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
const currAnchor = anchors[i];
|
|
236
|
+
if (!currAnchor) return null;
|
|
237
|
+
const usedOverride = penOverride;
|
|
238
|
+
penOverride = null;
|
|
239
|
+
if (step.kind === "line") {
|
|
240
|
+
const fromClip = usedOverride ?? clipForTarget(prev.step.to, currAnchor, nodeIndex);
|
|
241
|
+
const toClip = clipForTarget(step.to, prev.anchor, nodeIndex);
|
|
242
|
+
if (!fromClip || !toClip) return null;
|
|
243
|
+
startSegment(fromClip);
|
|
244
|
+
emitLine(toClip);
|
|
245
|
+
collectLabel(step, (t) => lineSegmentSample(fromClip, toClip, t));
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (step.kind === "curve") {
|
|
249
|
+
const fromClip = usedOverride ?? clipForTarget(prev.step.to, step.control, nodeIndex);
|
|
250
|
+
const toClip = clipForTarget(step.to, step.control, nodeIndex);
|
|
251
|
+
if (!fromClip || !toClip) return null;
|
|
252
|
+
startSegment(fromClip);
|
|
253
|
+
emitQuad(step.control, toClip);
|
|
254
|
+
collectLabel(step, (t) => quadSegmentSample(fromClip, step.control, toClip, t));
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (step.kind === "cubic") {
|
|
258
|
+
const fromClip = usedOverride ?? clipForTarget(prev.step.to, step.control1, nodeIndex);
|
|
259
|
+
const toClip = clipForTarget(step.to, step.control2, nodeIndex);
|
|
260
|
+
if (!fromClip || !toClip) return null;
|
|
261
|
+
startSegment(fromClip);
|
|
262
|
+
emitCubic(step.control1, step.control2, toClip);
|
|
263
|
+
collectLabel(step, (t) => cubicSegmentSample(fromClip, step.control1, step.control2, toClip, t));
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (step.kind === "bend") {
|
|
267
|
+
const angle = step.bendAngle ?? 30;
|
|
268
|
+
const [c1, c2] = bendControlPoints(prev.anchor, currAnchor, step.bendDirection, angle);
|
|
269
|
+
const fromClip = usedOverride ?? clipForTarget(prev.step.to, c1, nodeIndex);
|
|
270
|
+
const toClip = clipForTarget(step.to, c2, nodeIndex);
|
|
271
|
+
if (!fromClip || !toClip) return null;
|
|
272
|
+
startSegment(fromClip);
|
|
273
|
+
emitCubic(c1, c2, toClip);
|
|
274
|
+
collectLabel(step, (t) => cubicSegmentSample(fromClip, c1, c2, toClip, t));
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
const corner = cornerOf(prev.anchor, currAnchor, step.via);
|
|
278
|
+
const fromClip = usedOverride ?? clipForTarget(prev.step.to, corner, nodeIndex);
|
|
279
|
+
const toClip = clipForTarget(step.to, corner, nodeIndex);
|
|
280
|
+
if (!fromClip || !toClip) return null;
|
|
281
|
+
startSegment(fromClip);
|
|
282
|
+
emitLine(corner);
|
|
283
|
+
emitLine(toClip);
|
|
284
|
+
collectLabel(step, (t) => foldSegmentSample(fromClip, corner, toClip, t));
|
|
285
|
+
}
|
|
286
|
+
const strokeWidth = path.strokeWidth ?? (path.thickness ? THICKNESS_TO_WIDTH[path.thickness] : 1);
|
|
287
|
+
const baseProps = {
|
|
288
|
+
stroke: path.stroke ?? "currentColor",
|
|
289
|
+
strokeWidth,
|
|
290
|
+
fill: path.fill ?? "none",
|
|
291
|
+
fillRule: path.fillRule,
|
|
292
|
+
strokeDasharray: path.strokeDasharray,
|
|
293
|
+
strokeLinecap: path.lineCap,
|
|
294
|
+
strokeLinejoin: path.lineJoin,
|
|
295
|
+
opacity: path.opacity,
|
|
296
|
+
fillOpacity: path.fillOpacity,
|
|
297
|
+
strokeOpacity: path.drawOpacity
|
|
298
|
+
};
|
|
299
|
+
const markers = arrowMarkers(path.arrow, path.arrowDetail);
|
|
300
|
+
applyArrowShrinks(commands, markers.arrowStart ? computeShrink(markers.arrowStart) : 0, markers.arrowEnd ? computeShrink(markers.arrowEnd) : 0, strokeWidth, round);
|
|
301
|
+
const { primitive } = splitSubPathsForMarkers(commands, baseProps, markers);
|
|
302
|
+
return {
|
|
303
|
+
primitives: [primitive, ...labelPrims],
|
|
304
|
+
points
|
|
305
|
+
};
|
|
306
|
+
};
|
|
307
|
+
//#endregion
|
|
308
|
+
export { emitPathPrimitive };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { SegmentSample } from '../../geometry/segment';
|
|
2
|
+
import { IRPosition, IRStepLabel } from '../../ir';
|
|
3
|
+
import { ScenePrimitive } from '../../primitive';
|
|
4
|
+
import { TextMeasurer } from '../text-metrics';
|
|
5
|
+
/**
|
|
6
|
+
* label.position → 段参数 t∈[0,1]
|
|
7
|
+
* @description 数值原样返回(schema 已 clamp 0..1);keyword 走 KEYWORD_TO_T 映射;undefined 退默认 midway (0.5)
|
|
8
|
+
*/
|
|
9
|
+
export declare const tForLabelPosition: (pos: IRStepLabel["position"]) => number;
|
|
10
|
+
/**
|
|
11
|
+
* step.label + 段采样 → TextPrim(sloped 时裹一层 group 旋转)
|
|
12
|
+
* @description 默认 side='above'/position='midway':above/below 锚点 y±offset、align=middle、baseline=bottom/top;left/right x±offset、align=end/start、baseline=middle;sloped 不偏移裹 group rotate(angle, cx, cy) 由切线 atan2 算(SVG y-down CW 正)。返回 primitive + viewBox 外接点
|
|
13
|
+
*/
|
|
14
|
+
export declare const emitLabelPrimitive: (label: IRStepLabel, sample: SegmentSample, measureText: TextMeasurer, round: (n: number) => number) => {
|
|
15
|
+
primitive: ScenePrimitive;
|
|
16
|
+
points: Array<IRPosition>;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=label.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"label.d.ts","sourceRoot":"","sources":["../../../../src/compile/path/label.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAY,MAAM,iBAAiB,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAmBpD;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,KAAK,WAAW,CAAC,UAAU,CAAC,KAAG,MAIhE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAC7B,OAAO,WAAW,EAClB,QAAQ,aAAa,EACrB,aAAa,YAAY,EACzB,OAAO,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,KAC3B;IAAE,SAAS,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAA;CA8ExD,CAAC"}
|