@retikz/core 0.1.0 → 0.2.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/compile/anchor-cache.d.ts +12 -0
- package/dist/es/compile/anchor-cache.d.ts.map +1 -0
- package/dist/es/compile/anchor-cache.js +41 -0
- package/dist/es/compile/compile.d.ts +2 -2
- package/dist/es/compile/compile.d.ts.map +1 -1
- package/dist/es/compile/compile.js +150 -40
- package/dist/es/compile/index.d.ts +1 -0
- package/dist/es/compile/index.d.ts.map +1 -1
- package/dist/es/compile/name-stack.d.ts +81 -0
- package/dist/es/compile/name-stack.d.ts.map +1 -0
- package/dist/es/compile/name-stack.js +104 -0
- package/dist/es/compile/node.d.ts +9 -5
- package/dist/es/compile/node.d.ts.map +1 -1
- package/dist/es/compile/node.js +19 -11
- package/dist/es/compile/path/anchor.d.ts +11 -5
- package/dist/es/compile/path/anchor.d.ts.map +1 -1
- package/dist/es/compile/path/anchor.js +24 -13
- package/dist/es/compile/path/index.d.ts +9 -3
- package/dist/es/compile/path/index.d.ts.map +1 -1
- package/dist/es/compile/path/index.js +18 -17
- package/dist/es/compile/path/label.d.ts +1 -1
- package/dist/es/compile/path/label.d.ts.map +1 -1
- package/dist/es/compile/path/label.js +17 -4
- package/dist/es/compile/path/relative.d.ts +10 -4
- package/dist/es/compile/path/relative.d.ts.map +1 -1
- package/dist/es/compile/path/relative.js +16 -8
- package/dist/es/compile/position.d.ts +19 -3
- package/dist/es/compile/position.d.ts.map +1 -1
- package/dist/es/compile/position.js +28 -8
- package/dist/es/compile/scope.d.ts +66 -0
- package/dist/es/compile/scope.d.ts.map +1 -0
- package/dist/es/compile/scope.js +256 -0
- package/dist/es/compile/style.d.ts +46 -0
- package/dist/es/compile/style.d.ts.map +1 -0
- package/dist/es/compile/style.js +259 -0
- package/dist/es/index.d.ts +2 -2
- package/dist/es/index.d.ts.map +1 -1
- package/dist/es/index.js +3 -1
- 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 +5 -2
- package/dist/es/ir/node.d.ts.map +1 -1
- package/dist/es/ir/node.js +1 -0
- package/dist/es/ir/path/path.d.ts +531 -0
- package/dist/es/ir/path/path.d.ts.map +1 -1
- package/dist/es/ir/path/path.js +1 -0
- package/dist/es/ir/path/step.d.ts +834 -0
- package/dist/es/ir/path/step.d.ts.map +1 -1
- package/dist/es/ir/path/step.js +5 -1
- package/dist/es/ir/scene.d.ts +18 -3542
- package/dist/es/ir/scene.d.ts.map +1 -1
- package/dist/es/ir/scene.js +11 -3
- package/dist/es/ir/scope.d.ts +3690 -0
- package/dist/es/ir/scope.d.ts.map +1 -0
- package/dist/es/ir/scope.js +89 -0
- package/dist/es/ir/transform.d.ts +204 -0
- package/dist/es/ir/transform.d.ts.map +1 -0
- package/dist/es/ir/transform.js +56 -0
- package/dist/lib/compile/anchor-cache.cjs +41 -0
- package/dist/lib/compile/anchor-cache.d.ts +12 -0
- package/dist/lib/compile/anchor-cache.d.ts.map +1 -0
- package/dist/lib/compile/compile.cjs +150 -40
- package/dist/lib/compile/compile.d.ts +2 -2
- package/dist/lib/compile/compile.d.ts.map +1 -1
- package/dist/lib/compile/index.d.ts +1 -0
- package/dist/lib/compile/index.d.ts.map +1 -1
- package/dist/lib/compile/name-stack.cjs +104 -0
- package/dist/lib/compile/name-stack.d.ts +81 -0
- package/dist/lib/compile/name-stack.d.ts.map +1 -0
- package/dist/lib/compile/node.cjs +19 -11
- package/dist/lib/compile/node.d.ts +9 -5
- package/dist/lib/compile/node.d.ts.map +1 -1
- package/dist/lib/compile/path/anchor.cjs +23 -12
- package/dist/lib/compile/path/anchor.d.ts +11 -5
- package/dist/lib/compile/path/anchor.d.ts.map +1 -1
- package/dist/lib/compile/path/index.cjs +18 -17
- package/dist/lib/compile/path/index.d.ts +9 -3
- package/dist/lib/compile/path/index.d.ts.map +1 -1
- package/dist/lib/compile/path/label.cjs +17 -4
- package/dist/lib/compile/path/label.d.ts +1 -1
- package/dist/lib/compile/path/label.d.ts.map +1 -1
- package/dist/lib/compile/path/relative.cjs +16 -8
- package/dist/lib/compile/path/relative.d.ts +10 -4
- package/dist/lib/compile/path/relative.d.ts.map +1 -1
- package/dist/lib/compile/position.cjs +28 -8
- package/dist/lib/compile/position.d.ts +19 -3
- package/dist/lib/compile/position.d.ts.map +1 -1
- package/dist/lib/compile/scope.cjs +261 -0
- package/dist/lib/compile/scope.d.ts +66 -0
- package/dist/lib/compile/scope.d.ts.map +1 -0
- package/dist/lib/compile/style.cjs +262 -0
- package/dist/lib/compile/style.d.ts +46 -0
- package/dist/lib/compile/style.d.ts.map +1 -0
- package/dist/lib/index.cjs +8 -0
- package/dist/lib/index.d.ts +2 -2
- package/dist/lib/index.d.ts.map +1 -1
- 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 +1 -0
- package/dist/lib/ir/node.d.ts +5 -2
- package/dist/lib/ir/node.d.ts.map +1 -1
- package/dist/lib/ir/path/path.cjs +1 -0
- package/dist/lib/ir/path/path.d.ts +531 -0
- package/dist/lib/ir/path/path.d.ts.map +1 -1
- package/dist/lib/ir/path/step.cjs +5 -1
- package/dist/lib/ir/path/step.d.ts +834 -0
- package/dist/lib/ir/path/step.d.ts.map +1 -1
- package/dist/lib/ir/scene.cjs +11 -3
- package/dist/lib/ir/scene.d.ts +18 -3542
- package/dist/lib/ir/scene.d.ts.map +1 -1
- package/dist/lib/ir/scope.cjs +94 -0
- package/dist/lib/ir/scope.d.ts +3690 -0
- package/dist/lib/ir/scope.d.ts.map +1 -0
- package/dist/lib/ir/transform.cjs +56 -0
- package/dist/lib/ir/transform.d.ts +204 -0
- package/dist/lib/ir/transform.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/es/compile/node.js
CHANGED
|
@@ -130,11 +130,16 @@ var labelCenter = (layout, label) => {
|
|
|
130
130
|
};
|
|
131
131
|
/**
|
|
132
132
|
* 取节点 shape 在指定角度方向的边界点
|
|
133
|
-
* @description
|
|
133
|
+
* @description 角度是节点**局部坐标系**下的极角(度数:0°=局部 +x,90°=局部 +y)。layout.rect.rotate 把局部基绕中心旋转,得到世界系下的视觉方向;shape boundaryPoint 内部用 rotate-aware 投影,所以这里把局部 (cos, sin) 经 rect.rotate 旋转后加到中心当作世界系 toward 传入。不应用 margin(同 anchorOf);用于 `'A.30'` 落点
|
|
134
134
|
*/
|
|
135
135
|
var angleBoundaryOf = (layout, angleDeg) => {
|
|
136
136
|
const rad = angleDeg * Math.PI / 180;
|
|
137
|
-
const
|
|
137
|
+
const lx = Math.cos(rad);
|
|
138
|
+
const ly = Math.sin(rad);
|
|
139
|
+
const rot = layout.rect.rotate ?? 0;
|
|
140
|
+
const cosR = Math.cos(rot);
|
|
141
|
+
const sinR = Math.sin(rot);
|
|
142
|
+
const toward = [layout.rect.x + lx * cosR - ly * sinR, layout.rect.y + lx * sinR + ly * cosR];
|
|
138
143
|
switch (layout.shape) {
|
|
139
144
|
case "rectangle": return rect.boundaryPoint(rectOf(layout, 0), toward);
|
|
140
145
|
case "circle": return circle.boundaryPoint(circleOf(layout, 0), toward);
|
|
@@ -144,9 +149,12 @@ var angleBoundaryOf = (layout, angleDeg) => {
|
|
|
144
149
|
};
|
|
145
150
|
/**
|
|
146
151
|
* IR Node → 内部 NodeLayout
|
|
147
|
-
* @description 文本度量 + padding 推内框半轴;按 shape 算外接边界(circle 取半对角线、ellipse ×√2、diamond ×2);解析 position 为几何中心;rotate
|
|
152
|
+
* @description 文本度量 + padding 推内框半轴;按 shape 算外接边界(circle 取半对角线、ellipse ×√2、diamond ×2);解析 position 为几何中心;rotate 度数转弧度。
|
|
153
|
+
* `scopeChain` 非空时 `resolvePosition` 返回**当前 scope 局部坐标**(relative position 在当前
|
|
154
|
+
* scope 局部度量),调用方负责后续 `projectLayoutToGlobal` / `applyTransformChain` 投回全局;
|
|
155
|
+
* 笛卡尔字面量 `Position` 已在 scope 局部度量,行为延续 v0.1。
|
|
148
156
|
*/
|
|
149
|
-
var layoutNode = (node, measureText,
|
|
157
|
+
var layoutNode = (node, measureText, nameStack, nodeDistance, scopeChain = [], labelDefault) => {
|
|
150
158
|
const sx = node.xScale ?? node.scale ?? 1;
|
|
151
159
|
const sy = node.yScale ?? node.scale ?? 1;
|
|
152
160
|
const fontScale = Math.min(sx, sy);
|
|
@@ -217,7 +225,7 @@ var layoutNode = (node, measureText, nodeIndex, nodeDistance) => {
|
|
|
217
225
|
break;
|
|
218
226
|
}
|
|
219
227
|
const rotateDeg = node.rotate ?? 0;
|
|
220
|
-
const center = resolvePosition(node.position,
|
|
228
|
+
const center = resolvePosition(node.position, nameStack, nodeDistance, scopeChain);
|
|
221
229
|
if (!center) throw new Error(`Cannot resolve position for node ${node.id ?? "(unnamed)"}; polar.origin or at.of may reference an undefined node`);
|
|
222
230
|
const labels = (node.label === void 0 ? void 0 : Array.isArray(node.label) ? node.label : [node.label])?.map((lab) => {
|
|
223
231
|
const labFont = lab.font;
|
|
@@ -225,12 +233,12 @@ var layoutNode = (node, measureText, nodeIndex, nodeDistance) => {
|
|
|
225
233
|
text: lab.text,
|
|
226
234
|
position: lab.position ?? "above",
|
|
227
235
|
distance: lab.distance ?? DEFAULT_LABEL_DISTANCE,
|
|
228
|
-
textColor: lab.textColor ?? node.textColor,
|
|
229
|
-
opacity: lab.opacity,
|
|
230
|
-
fontSize: (labFont?.size ?? baseFontSize) * fontScale,
|
|
231
|
-
fontFamily: labFont?.family ?? fontFamily,
|
|
232
|
-
fontWeight: labFont?.weight ?? fontWeight,
|
|
233
|
-
fontStyle: labFont?.style ?? fontStyle
|
|
236
|
+
textColor: lab.textColor ?? labelDefault?.textColor ?? labelDefault?.color ?? node.textColor,
|
|
237
|
+
opacity: lab.opacity ?? labelDefault?.opacity,
|
|
238
|
+
fontSize: (labFont?.size ?? labelDefault?.font?.size ?? baseFontSize) * fontScale,
|
|
239
|
+
fontFamily: labFont?.family ?? labelDefault?.font?.family ?? fontFamily,
|
|
240
|
+
fontWeight: labFont?.weight ?? labelDefault?.font?.weight ?? fontWeight,
|
|
241
|
+
fontStyle: labFont?.style ?? labelDefault?.font?.style ?? fontStyle
|
|
234
242
|
};
|
|
235
243
|
});
|
|
236
244
|
return {
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import { IRPosition, IRTarget } from '../../ir';
|
|
2
|
-
import {
|
|
2
|
+
import { Transform } from '../../primitive';
|
|
3
|
+
import { NameStack } from '../name-stack';
|
|
3
4
|
/**
|
|
4
5
|
* 求 step.to 的参考点(给 boundary clip 算方向 / 折角 corner 用)
|
|
5
|
-
* @description 三态:`'A'`(auto) 节点中心;`'A.<anchor>'`/`'A.<deg>'` 显式锚点 refPoint=endpoint
|
|
6
|
+
* @description 三态:`'A'`(auto) 节点中心;`'A.<anchor>'`/`'A.<deg>'` 显式锚点 refPoint=endpoint 位置不随邻居变。直接坐标/极坐标解析为笛卡尔。
|
|
7
|
+
* string id lookup 拿到的 layout 已是全局坐标——不走 scopeChain 投影;Position / Polar /
|
|
8
|
+
* At / Offset 字面量经 `resolvePosition(..., scopeChain)` 拿到当前 scope 局部坐标后
|
|
9
|
+
* `applyTransformChain` 投回全局。`scopeChain=[]` 等价 v0.1(恒等)。
|
|
6
10
|
*/
|
|
7
|
-
export declare const refPointOfTarget: (target: IRTarget,
|
|
11
|
+
export declare const refPointOfTarget: (target: IRTarget, nameStack: NameStack, scopeChain?: ReadonlyArray<Transform>) => IRPosition | null;
|
|
8
12
|
/** 折角中间点:`-|` → (curr.x, prev.y);`|-` → (prev.x, curr.y) */
|
|
9
13
|
export declare const cornerOf: (prev: IRPosition, curr: IRPosition, via: "-|" | "|-") => IRPosition;
|
|
10
14
|
/**
|
|
11
15
|
* 在 toward 方向算 step.to 的实际绘制端点
|
|
12
|
-
* @description 节点 auto `'A'`:按 shape 走 boundaryPointOf 求中心→toward 射线交点;命名 anchor/角度:位置已定不受 toward 影响;直接坐标/极坐标:解析后返回;失败返回 null
|
|
16
|
+
* @description 节点 auto `'A'`:按 shape 走 boundaryPointOf 求中心→toward 射线交点;命名 anchor/角度:位置已定不受 toward 影响;直接坐标/极坐标:解析后返回;失败返回 null。
|
|
17
|
+
* string id lookup 拿到的 layout 已是全局坐标;Position / Polar / At / Offset 字面量经
|
|
18
|
+
* `resolvePosition(..., scopeChain)` 拿到当前 scope 局部坐标后 `applyTransformChain` 投回全局。
|
|
13
19
|
*/
|
|
14
|
-
export declare const clipForTarget: (target: IRTarget, toward: IRPosition,
|
|
20
|
+
export declare const clipForTarget: (target: IRTarget, toward: IRPosition, nameStack: NameStack, scopeChain?: ReadonlyArray<Transform>) => IRPosition | null;
|
|
15
21
|
/** 两个 IRPosition 两分量精确相等(未 round) */
|
|
16
22
|
export declare const samePoint: (a: IRPosition | null, b: IRPosition | null) => boolean;
|
|
17
23
|
/** 把 p 朝 target 方向移动 dist */
|
|
@@ -1 +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,
|
|
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,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAM/C;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,QAAQ,EAChB,WAAW,SAAS,EACpB,aAAY,aAAa,CAAC,SAAS,CAAM,KACxC,UAAU,GAAG,IAqBf,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,QAAQ,GACnB,MAAM,UAAU,EAChB,MAAM,UAAU,EAChB,KAAK,IAAI,GAAG,IAAI,KACf,UACqD,CAAC;AAEzD;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GACxB,QAAQ,QAAQ,EAChB,QAAQ,UAAU,EAClB,WAAW,SAAS,EACpB,aAAY,aAAa,CAAC,SAAS,CAAM,KACxC,UAAU,GAAG,IAqBf,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"}
|
|
@@ -1,44 +1,55 @@
|
|
|
1
|
+
import { applyTransformChain } from "../scope.js";
|
|
1
2
|
import { resolvePosition } from "../position.js";
|
|
2
|
-
import {
|
|
3
|
+
import { boundaryPointOf } from "../node.js";
|
|
4
|
+
import { resolveAnchor } from "../anchor-cache.js";
|
|
3
5
|
import { parseNodeRef } from "../parseTarget.js";
|
|
4
6
|
//#region src/compile/path/anchor.ts
|
|
5
7
|
/**
|
|
6
8
|
* 求 step.to 的参考点(给 boundary clip 算方向 / 折角 corner 用)
|
|
7
|
-
* @description 三态:`'A'`(auto) 节点中心;`'A.<anchor>'`/`'A.<deg>'` 显式锚点 refPoint=endpoint
|
|
9
|
+
* @description 三态:`'A'`(auto) 节点中心;`'A.<anchor>'`/`'A.<deg>'` 显式锚点 refPoint=endpoint 位置不随邻居变。直接坐标/极坐标解析为笛卡尔。
|
|
10
|
+
* string id lookup 拿到的 layout 已是全局坐标——不走 scopeChain 投影;Position / Polar /
|
|
11
|
+
* At / Offset 字面量经 `resolvePosition(..., scopeChain)` 拿到当前 scope 局部坐标后
|
|
12
|
+
* `applyTransformChain` 投回全局。`scopeChain=[]` 等价 v0.1(恒等)。
|
|
8
13
|
*/
|
|
9
|
-
var refPointOfTarget = (target,
|
|
14
|
+
var refPointOfTarget = (target, nameStack, scopeChain = []) => {
|
|
10
15
|
if (typeof target === "string") {
|
|
11
16
|
const ref = parseNodeRef(target);
|
|
12
|
-
const node =
|
|
17
|
+
const node = nameStack.lookup(ref.id);
|
|
13
18
|
if (!node) return null;
|
|
14
19
|
switch (ref.kind) {
|
|
15
20
|
case "node": return [node.rect.x, node.rect.y];
|
|
16
|
-
case "anchor": return
|
|
17
|
-
case "angle": return
|
|
21
|
+
case "anchor": return resolveAnchor(node, ref.anchor);
|
|
22
|
+
case "angle": return resolveAnchor(node, String(ref.angle));
|
|
18
23
|
}
|
|
19
24
|
}
|
|
20
25
|
if (typeof target === "object" && !Array.isArray(target) && ("relative" in target || "relativeAccumulate" in target)) return null;
|
|
21
|
-
|
|
26
|
+
const local = resolvePosition(target, nameStack, void 0, scopeChain);
|
|
27
|
+
if (!local) return null;
|
|
28
|
+
return scopeChain.length === 0 ? local : applyTransformChain(local, scopeChain);
|
|
22
29
|
};
|
|
23
30
|
/** 折角中间点:`-|` → (curr.x, prev.y);`|-` → (prev.x, curr.y) */
|
|
24
31
|
var cornerOf = (prev, curr, via) => via === "-|" ? [curr[0], prev[1]] : [prev[0], curr[1]];
|
|
25
32
|
/**
|
|
26
33
|
* 在 toward 方向算 step.to 的实际绘制端点
|
|
27
|
-
* @description 节点 auto `'A'`:按 shape 走 boundaryPointOf 求中心→toward 射线交点;命名 anchor/角度:位置已定不受 toward 影响;直接坐标/极坐标:解析后返回;失败返回 null
|
|
34
|
+
* @description 节点 auto `'A'`:按 shape 走 boundaryPointOf 求中心→toward 射线交点;命名 anchor/角度:位置已定不受 toward 影响;直接坐标/极坐标:解析后返回;失败返回 null。
|
|
35
|
+
* string id lookup 拿到的 layout 已是全局坐标;Position / Polar / At / Offset 字面量经
|
|
36
|
+
* `resolvePosition(..., scopeChain)` 拿到当前 scope 局部坐标后 `applyTransformChain` 投回全局。
|
|
28
37
|
*/
|
|
29
|
-
var clipForTarget = (target, toward,
|
|
38
|
+
var clipForTarget = (target, toward, nameStack, scopeChain = []) => {
|
|
30
39
|
if (typeof target === "string") {
|
|
31
40
|
const ref = parseNodeRef(target);
|
|
32
|
-
const node =
|
|
41
|
+
const node = nameStack.lookup(ref.id);
|
|
33
42
|
if (!node) return null;
|
|
34
43
|
switch (ref.kind) {
|
|
35
44
|
case "node": return boundaryPointOf(node, toward);
|
|
36
|
-
case "anchor": return
|
|
37
|
-
case "angle": return
|
|
45
|
+
case "anchor": return resolveAnchor(node, ref.anchor);
|
|
46
|
+
case "angle": return resolveAnchor(node, String(ref.angle));
|
|
38
47
|
}
|
|
39
48
|
}
|
|
40
49
|
if (typeof target === "object" && !Array.isArray(target) && ("relative" in target || "relativeAccumulate" in target)) return null;
|
|
41
|
-
|
|
50
|
+
const local = resolvePosition(target, nameStack, void 0, scopeChain);
|
|
51
|
+
if (!local) return null;
|
|
52
|
+
return scopeChain.length === 0 ? local : applyTransformChain(local, scopeChain);
|
|
42
53
|
};
|
|
43
54
|
/** 两个 IRPosition 两分量精确相等(未 round) */
|
|
44
55
|
var samePoint = (a, b) => !!a && !!b && a[0] === b[0] && a[1] === b[1];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { IRPath, IRPosition } from '../../ir';
|
|
2
|
-
import { ScenePrimitive } from '../../primitive';
|
|
3
|
-
import {
|
|
2
|
+
import { ScenePrimitive, Transform } from '../../primitive';
|
|
3
|
+
import { NameStack } from '../name-stack';
|
|
4
4
|
import { TextMeasurer } from '../text-metrics';
|
|
5
5
|
/** emitPathPrimitive 可选 warn 钩子 */
|
|
6
6
|
export type EmitPathWarnHook = {
|
|
@@ -12,12 +12,18 @@ export type EmitPathWarnHook = {
|
|
|
12
12
|
}) => void;
|
|
13
13
|
/** 当前 path 在 IR 中的 locator 前缀(如 `'children[3].path'`) */
|
|
14
14
|
irPath?: string;
|
|
15
|
+
/**
|
|
16
|
+
* 该 path 所属 scope 的累积 Cartesian-only transform 链
|
|
17
|
+
* @description step.to 内的 polar/at/offset 字面量按"当前 scope 局部度量 + 末端 apply chain"
|
|
18
|
+
* 投影回全局;顶层 path / 无 scope chain 时为 `[]`(恒等,等价 v0.1 行为)
|
|
19
|
+
*/
|
|
20
|
+
scopeChain?: ReadonlyArray<Transform>;
|
|
15
21
|
};
|
|
16
22
|
/**
|
|
17
23
|
* IR Path → PathPrim
|
|
18
24
|
* @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
|
|
19
25
|
*/
|
|
20
|
-
export declare const emitPathPrimitive: (path: IRPath,
|
|
26
|
+
export declare const emitPathPrimitive: (path: IRPath, nameStack: NameStack, round: (n: number) => number, measureText?: TextMeasurer, warnHook?: EmitPathWarnHook) => {
|
|
21
27
|
primitives: Array<ScenePrimitive>;
|
|
22
28
|
points: Array<IRPosition>;
|
|
23
29
|
} | null;
|
|
@@ -1 +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,
|
|
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,EACd,SAAS,EACV,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,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;IAChB;;;;OAIG;IACH,UAAU,CAAC,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;CACvC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAC5B,MAAM,MAAM,EACZ,WAAW,SAAS,EACpB,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,IA2XrE,CAAC"}
|
|
@@ -26,7 +26,7 @@ var THICKNESS_TO_WIDTH = {
|
|
|
26
26
|
* IR Path → PathPrim
|
|
27
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
28
|
*/
|
|
29
|
-
var emitPathPrimitive = (path,
|
|
29
|
+
var emitPathPrimitive = (path, nameStack, round, measureText = fallbackMeasurer, warnHook = {}) => {
|
|
30
30
|
const irPath = warnHook.irPath ?? "path";
|
|
31
31
|
const warn = (code, message, subPath = "") => {
|
|
32
32
|
warnHook.onWarn?.({
|
|
@@ -35,7 +35,8 @@ var emitPathPrimitive = (path, nodeIndex, round, measureText = fallbackMeasurer,
|
|
|
35
35
|
path: subPath ? `${irPath}.${subPath}` : irPath
|
|
36
36
|
});
|
|
37
37
|
};
|
|
38
|
-
const
|
|
38
|
+
const scopeChain = warnHook.scopeChain ?? [];
|
|
39
|
+
const steps = normalizeRelativeTargets(path.children, nameStack, scopeChain);
|
|
39
40
|
if (steps.length < 2) {
|
|
40
41
|
warn("PATH_TOO_SHORT", `Path requires at least 2 steps (got ${steps.length}); the entire path is skipped`, "children");
|
|
41
42
|
return null;
|
|
@@ -46,14 +47,14 @@ var emitPathPrimitive = (path, nodeIndex, round, measureText = fallbackMeasurer,
|
|
|
46
47
|
const collectLabel = (step, sampleAt) => {
|
|
47
48
|
if (step.kind === "move" || step.kind === "cycle" || !("label" in step) || !step.label) return;
|
|
48
49
|
const sample = sampleAt(tForLabelPosition(step.label.position));
|
|
49
|
-
const r = emitLabelPrimitive(step.label, sample, measureText, round);
|
|
50
|
+
const r = emitLabelPrimitive(step.label, sample, measureText, round, path.opacity);
|
|
50
51
|
labelPrims.push(r.primitive);
|
|
51
52
|
for (const p of r.points) points.push(p);
|
|
52
53
|
};
|
|
53
54
|
const hasTo = (s) => s.kind !== "cycle" && s.kind !== "arc" && s.kind !== "circlePath" && s.kind !== "ellipsePath";
|
|
54
55
|
const anchors = steps.map((s, idx) => {
|
|
55
56
|
if (!hasTo(s)) return null;
|
|
56
|
-
const ref = refPointOfTarget(s.to,
|
|
57
|
+
const ref = refPointOfTarget(s.to, nameStack, scopeChain);
|
|
57
58
|
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
59
|
return ref;
|
|
59
60
|
});
|
|
@@ -179,10 +180,10 @@ var emitPathPrimitive = (path, nodeIndex, round, measureText = fallbackMeasurer,
|
|
|
179
180
|
const moveTo = lastMoveTo;
|
|
180
181
|
const prev = findPrev();
|
|
181
182
|
if (!moveTo || !prev) continue;
|
|
182
|
-
const moveAnchor = refPointOfTarget(moveTo,
|
|
183
|
+
const moveAnchor = refPointOfTarget(moveTo, nameStack, scopeChain);
|
|
183
184
|
if (!moveAnchor) return null;
|
|
184
|
-
const fromClip = clipForTarget(prev.step.to, moveAnchor,
|
|
185
|
-
const toClip = clipForTarget(moveTo, prev.anchor,
|
|
185
|
+
const fromClip = clipForTarget(prev.step.to, moveAnchor, nameStack, scopeChain);
|
|
186
|
+
const toClip = clipForTarget(moveTo, prev.anchor, nameStack, scopeChain);
|
|
186
187
|
if (!fromClip || !toClip) return null;
|
|
187
188
|
if (samePoint(fromClip, lastEnd) && samePoint(toClip, subPathStart)) {
|
|
188
189
|
emitClose();
|
|
@@ -237,8 +238,8 @@ var emitPathPrimitive = (path, nodeIndex, round, measureText = fallbackMeasurer,
|
|
|
237
238
|
const usedOverride = penOverride;
|
|
238
239
|
penOverride = null;
|
|
239
240
|
if (step.kind === "line") {
|
|
240
|
-
const fromClip = usedOverride ?? clipForTarget(prev.step.to, currAnchor,
|
|
241
|
-
const toClip = clipForTarget(step.to, prev.anchor,
|
|
241
|
+
const fromClip = usedOverride ?? clipForTarget(prev.step.to, currAnchor, nameStack, scopeChain);
|
|
242
|
+
const toClip = clipForTarget(step.to, prev.anchor, nameStack, scopeChain);
|
|
242
243
|
if (!fromClip || !toClip) return null;
|
|
243
244
|
startSegment(fromClip);
|
|
244
245
|
emitLine(toClip);
|
|
@@ -246,8 +247,8 @@ var emitPathPrimitive = (path, nodeIndex, round, measureText = fallbackMeasurer,
|
|
|
246
247
|
continue;
|
|
247
248
|
}
|
|
248
249
|
if (step.kind === "curve") {
|
|
249
|
-
const fromClip = usedOverride ?? clipForTarget(prev.step.to, step.control,
|
|
250
|
-
const toClip = clipForTarget(step.to, step.control,
|
|
250
|
+
const fromClip = usedOverride ?? clipForTarget(prev.step.to, step.control, nameStack, scopeChain);
|
|
251
|
+
const toClip = clipForTarget(step.to, step.control, nameStack, scopeChain);
|
|
251
252
|
if (!fromClip || !toClip) return null;
|
|
252
253
|
startSegment(fromClip);
|
|
253
254
|
emitQuad(step.control, toClip);
|
|
@@ -255,8 +256,8 @@ var emitPathPrimitive = (path, nodeIndex, round, measureText = fallbackMeasurer,
|
|
|
255
256
|
continue;
|
|
256
257
|
}
|
|
257
258
|
if (step.kind === "cubic") {
|
|
258
|
-
const fromClip = usedOverride ?? clipForTarget(prev.step.to, step.control1,
|
|
259
|
-
const toClip = clipForTarget(step.to, step.control2,
|
|
259
|
+
const fromClip = usedOverride ?? clipForTarget(prev.step.to, step.control1, nameStack, scopeChain);
|
|
260
|
+
const toClip = clipForTarget(step.to, step.control2, nameStack, scopeChain);
|
|
260
261
|
if (!fromClip || !toClip) return null;
|
|
261
262
|
startSegment(fromClip);
|
|
262
263
|
emitCubic(step.control1, step.control2, toClip);
|
|
@@ -266,8 +267,8 @@ var emitPathPrimitive = (path, nodeIndex, round, measureText = fallbackMeasurer,
|
|
|
266
267
|
if (step.kind === "bend") {
|
|
267
268
|
const angle = step.bendAngle ?? 30;
|
|
268
269
|
const [c1, c2] = bendControlPoints(prev.anchor, currAnchor, step.bendDirection, angle);
|
|
269
|
-
const fromClip = usedOverride ?? clipForTarget(prev.step.to, c1,
|
|
270
|
-
const toClip = clipForTarget(step.to, c2,
|
|
270
|
+
const fromClip = usedOverride ?? clipForTarget(prev.step.to, c1, nameStack, scopeChain);
|
|
271
|
+
const toClip = clipForTarget(step.to, c2, nameStack, scopeChain);
|
|
271
272
|
if (!fromClip || !toClip) return null;
|
|
272
273
|
startSegment(fromClip);
|
|
273
274
|
emitCubic(c1, c2, toClip);
|
|
@@ -275,8 +276,8 @@ var emitPathPrimitive = (path, nodeIndex, round, measureText = fallbackMeasurer,
|
|
|
275
276
|
continue;
|
|
276
277
|
}
|
|
277
278
|
const corner = cornerOf(prev.anchor, currAnchor, step.via);
|
|
278
|
-
const fromClip = usedOverride ?? clipForTarget(prev.step.to, corner,
|
|
279
|
-
const toClip = clipForTarget(step.to, corner,
|
|
279
|
+
const fromClip = usedOverride ?? clipForTarget(prev.step.to, corner, nameStack, scopeChain);
|
|
280
|
+
const toClip = clipForTarget(step.to, corner, nameStack, scopeChain);
|
|
280
281
|
if (!fromClip || !toClip) return null;
|
|
281
282
|
startSegment(fromClip);
|
|
282
283
|
emitLine(corner);
|
|
@@ -11,7 +11,7 @@ export declare const tForLabelPosition: (pos: IRStepLabel["position"]) => number
|
|
|
11
11
|
* step.label + 段采样 → TextPrim(sloped 时裹一层 group 旋转)
|
|
12
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 + layout 外接点
|
|
13
13
|
*/
|
|
14
|
-
export declare const emitLabelPrimitive: (label: IRStepLabel, sample: SegmentSample, measureText: TextMeasurer, round: (n: number) => number) => {
|
|
14
|
+
export declare const emitLabelPrimitive: (label: IRStepLabel, sample: SegmentSample, measureText: TextMeasurer, round: (n: number) => number, hostOpacity?: number) => {
|
|
15
15
|
primitive: ScenePrimitive;
|
|
16
16
|
points: Array<IRPosition>;
|
|
17
17
|
};
|
|
@@ -1 +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,
|
|
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,EAC5B,cAAc,MAAM,KACnB;IAAE,SAAS,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAA;CAkGxD,CAAC"}
|
|
@@ -27,13 +27,22 @@ var tForLabelPosition = (pos) => {
|
|
|
27
27
|
* step.label + 段采样 → TextPrim(sloped 时裹一层 group 旋转)
|
|
28
28
|
* @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 + layout 外接点
|
|
29
29
|
*/
|
|
30
|
-
var emitLabelPrimitive = (label, sample, measureText, round) => {
|
|
31
|
-
const fontSize = LABEL_FONT_SIZE;
|
|
30
|
+
var emitLabelPrimitive = (label, sample, measureText, round, hostOpacity) => {
|
|
31
|
+
const fontSize = label.font?.size ?? LABEL_FONT_SIZE;
|
|
32
|
+
const fontFamily = label.font?.family;
|
|
33
|
+
const fontWeight = label.font?.weight;
|
|
34
|
+
const fontStyle = label.font?.style;
|
|
32
35
|
const lineHeight = fontSize * LABEL_LINE_HEIGHT_FACTOR;
|
|
33
|
-
const m = measureText(label.text, {
|
|
36
|
+
const m = measureText(label.text, {
|
|
37
|
+
size: fontSize,
|
|
38
|
+
family: fontFamily,
|
|
39
|
+
weight: fontWeight,
|
|
40
|
+
style: fontStyle
|
|
41
|
+
});
|
|
34
42
|
const measuredWidth = m.width;
|
|
35
43
|
const measuredHeight = m.height || lineHeight;
|
|
36
44
|
const side = label.side ?? "above";
|
|
45
|
+
const labelOpacity = label.opacity !== void 0 ? hostOpacity !== void 0 ? label.opacity * hostOpacity : label.opacity : hostOpacity;
|
|
37
46
|
let x = sample.point[0];
|
|
38
47
|
let y = sample.point[1];
|
|
39
48
|
let align = "middle";
|
|
@@ -62,8 +71,12 @@ var emitLabelPrimitive = (label, sample, measureText, round) => {
|
|
|
62
71
|
lineHeight: round(lineHeight),
|
|
63
72
|
measuredWidth: round(measuredWidth),
|
|
64
73
|
measuredHeight: round(measuredHeight),
|
|
65
|
-
fill: "currentColor"
|
|
74
|
+
fill: label.textColor ?? "currentColor"
|
|
66
75
|
};
|
|
76
|
+
if (fontFamily !== void 0) text.fontFamily = fontFamily;
|
|
77
|
+
if (fontWeight !== void 0) text.fontWeight = fontWeight;
|
|
78
|
+
if (fontStyle !== void 0) text.fontStyle = fontStyle;
|
|
79
|
+
if (labelOpacity !== void 0) text.opacity = labelOpacity;
|
|
67
80
|
if (side === "sloped") {
|
|
68
81
|
const groupPrim = {
|
|
69
82
|
type: "group",
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { IRStep } from '../../ir';
|
|
2
|
-
import {
|
|
2
|
+
import { Transform } from '../../primitive';
|
|
3
|
+
import { NameStack } from '../name-stack';
|
|
3
4
|
/**
|
|
4
|
-
* relative/relativeAccumulate 目标解析为绝对 Position(step kind 不变,to
|
|
5
|
-
* @description relative 不更新 prevEnd(TikZ `+`),relativeAccumulate 更新(TikZ `++`)。prevEnd 推进:有 to 的 kind 用 refPointOfTarget(to);arc 用 arcEndPoint;circlePath/ellipsePath/cycle 不变。首步 relative 时 prevEnd 回退 [0,0];解析失败保持原 step
|
|
5
|
+
* relative/relativeAccumulate 目标解析为绝对 Position(step kind 不变,to 为局部坐标 tuple)
|
|
6
|
+
* @description relative 不更新 prevEnd(TikZ `+`),relativeAccumulate 更新(TikZ `++`)。prevEnd 推进:有 to 的 kind 用 refPointOfTarget(to);arc 用 arcEndPoint;circlePath/ellipsePath/cycle 不变。首步 relative 时 prevEnd 回退 [0,0];解析失败保持原 step。
|
|
7
|
+
* prevEnd 始终是全局坐标系下的 cursor;relative 形态 `[dx, dy]` 在**当前 scope 局部度量**——
|
|
8
|
+
* 先反向投影 prevEnd 到 scope 局部 + 加 (dx, dy) 得局部 tuple,写回 step.to。下游
|
|
9
|
+
* `refPointOfTarget` / `clipForTarget` 把 tuple 视作 scope 局部字面量,统一 `applyTransformChain` 投回全局——
|
|
10
|
+
* relative 分支只负责"折算到局部",不能在此处提前投影到全局,否则与下游 chain apply 形成 double-apply。
|
|
11
|
+
* `scopeChain=[]` 时 inverse 恒等,等价 v0.1 行为。
|
|
6
12
|
*/
|
|
7
|
-
export declare const normalizeRelativeTargets: (steps: ReadonlyArray<IRStep>,
|
|
13
|
+
export declare const normalizeRelativeTargets: (steps: ReadonlyArray<IRStep>, nameStack: NameStack, scopeChain?: ReadonlyArray<Transform>) => Array<IRStep>;
|
|
8
14
|
//# sourceMappingURL=relative.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relative.d.ts","sourceRoot":"","sources":["../../../../src/compile/path/relative.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAc,MAAM,EAAY,MAAM,UAAU,CAAC;AAC7D,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"relative.d.ts","sourceRoot":"","sources":["../../../../src/compile/path/relative.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAc,MAAM,EAAY,MAAM,UAAU,CAAC;AAC7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAI/C;;;;;;;;GAQG;AACH,eAAO,MAAM,wBAAwB,GACnC,OAAO,aAAa,CAAC,MAAM,CAAC,EAC5B,WAAW,SAAS,EACpB,aAAY,aAAa,CAAC,SAAS,CAAM,KACxC,KAAK,CAAC,MAAM,CAuEd,CAAC"}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
+
import { inverseTransformChain } from "../scope.js";
|
|
1
2
|
import { arcEndPoint } from "../../geometry/arc.js";
|
|
2
3
|
import { refPointOfTarget } from "./anchor.js";
|
|
3
4
|
//#region src/compile/path/relative.ts
|
|
4
5
|
/**
|
|
5
|
-
* relative/relativeAccumulate 目标解析为绝对 Position(step kind 不变,to
|
|
6
|
-
* @description relative 不更新 prevEnd(TikZ `+`),relativeAccumulate 更新(TikZ `++`)。prevEnd 推进:有 to 的 kind 用 refPointOfTarget(to);arc 用 arcEndPoint;circlePath/ellipsePath/cycle 不变。首步 relative 时 prevEnd 回退 [0,0];解析失败保持原 step
|
|
6
|
+
* relative/relativeAccumulate 目标解析为绝对 Position(step kind 不变,to 为局部坐标 tuple)
|
|
7
|
+
* @description relative 不更新 prevEnd(TikZ `+`),relativeAccumulate 更新(TikZ `++`)。prevEnd 推进:有 to 的 kind 用 refPointOfTarget(to);arc 用 arcEndPoint;circlePath/ellipsePath/cycle 不变。首步 relative 时 prevEnd 回退 [0,0];解析失败保持原 step。
|
|
8
|
+
* prevEnd 始终是全局坐标系下的 cursor;relative 形态 `[dx, dy]` 在**当前 scope 局部度量**——
|
|
9
|
+
* 先反向投影 prevEnd 到 scope 局部 + 加 (dx, dy) 得局部 tuple,写回 step.to。下游
|
|
10
|
+
* `refPointOfTarget` / `clipForTarget` 把 tuple 视作 scope 局部字面量,统一 `applyTransformChain` 投回全局——
|
|
11
|
+
* relative 分支只负责"折算到局部",不能在此处提前投影到全局,否则与下游 chain apply 形成 double-apply。
|
|
12
|
+
* `scopeChain=[]` 时 inverse 恒等,等价 v0.1 行为。
|
|
7
13
|
*/
|
|
8
|
-
var normalizeRelativeTargets = (steps,
|
|
14
|
+
var normalizeRelativeTargets = (steps, nameStack, scopeChain = []) => {
|
|
9
15
|
let prevEnd = null;
|
|
10
16
|
const out = [];
|
|
11
17
|
for (const step of steps) {
|
|
@@ -26,19 +32,21 @@ var normalizeRelativeTargets = (steps, nodeIndex) => {
|
|
|
26
32
|
let resolvedTo = original;
|
|
27
33
|
let updatePrevEnd = true;
|
|
28
34
|
if (typeof original === "object" && !Array.isArray(original) && "relative" in original) {
|
|
29
|
-
const
|
|
30
|
-
|
|
35
|
+
const refGlobal = prevEnd ?? [0, 0];
|
|
36
|
+
const refLocal = scopeChain.length === 0 ? refGlobal : inverseTransformChain(refGlobal, scopeChain);
|
|
37
|
+
resolvedTo = [refLocal[0] + original.relative[0], refLocal[1] + original.relative[1]];
|
|
31
38
|
updatePrevEnd = false;
|
|
32
39
|
} else if (typeof original === "object" && !Array.isArray(original) && "relativeAccumulate" in original) {
|
|
33
|
-
const
|
|
34
|
-
|
|
40
|
+
const refGlobal = prevEnd ?? [0, 0];
|
|
41
|
+
const refLocal = scopeChain.length === 0 ? refGlobal : inverseTransformChain(refGlobal, scopeChain);
|
|
42
|
+
resolvedTo = [refLocal[0] + original.relativeAccumulate[0], refLocal[1] + original.relativeAccumulate[1]];
|
|
35
43
|
}
|
|
36
44
|
out.push({
|
|
37
45
|
...step,
|
|
38
46
|
to: resolvedTo
|
|
39
47
|
});
|
|
40
48
|
if (updatePrevEnd) {
|
|
41
|
-
const pos = refPointOfTarget(resolvedTo,
|
|
49
|
+
const pos = refPointOfTarget(resolvedTo, nameStack, scopeChain);
|
|
42
50
|
if (pos) prevEnd = pos;
|
|
43
51
|
}
|
|
44
52
|
}
|
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
import { IRAtPosition, IROffsetPosition, IRPosition, PolarPosition } from '../ir';
|
|
2
|
-
import {
|
|
2
|
+
import { Transform } from '../primitive';
|
|
3
|
+
import { NameStack } from './name-stack';
|
|
3
4
|
/**
|
|
4
5
|
* IR 各种位置形态(笛卡尔/极坐标/相对定位/偏移定位/节点 id)→ 笛卡尔位置
|
|
5
|
-
* @description
|
|
6
|
+
* @description
|
|
7
|
+
* - **返回值语义**:当 `scopeChain` 非空时,返回**当前 scope 局部坐标系**下的笛卡尔位置;
|
|
8
|
+
* 调用方负责走 `applyTransformChain(local, scopeChain)` 投回全局。当 `scopeChain` 为空时
|
|
9
|
+
* 等价于 v0.1 行为(全局坐标)。
|
|
10
|
+
* - **referent 处理**:node id lookup 拿到的 layout.rect 已是全局坐标;本函数内部用
|
|
11
|
+
* `inverseTransformChain` 把全局 referent 反向投影到当前 scope 局部坐标系作为"在当前
|
|
12
|
+
* scope 局部的固定点"基准。relative 部分(polar 的 angle/radius、at 的 direction/distance、
|
|
13
|
+
* offset 的 dx/dy)在当前 scope 局部度量后加到 referent 局部坐标上。
|
|
14
|
+
* - **嵌套**:PolarPosition.origin / OffsetPosition.of 是嵌套 polar 时,递归调用传同样
|
|
15
|
+
* scopeChain——整条嵌套链都在当前 scope 局部度量。
|
|
16
|
+
* - **笛卡尔字面量**:`Position` 形态直接返回(v0.1 行为延续:scope 内笛卡尔字面量在
|
|
17
|
+
* 当前 scope 局部度量;调用方走 applyTransformChain 投全局)。
|
|
18
|
+
* - 极坐标 origin / 偏移定位 of 均可递归引用节点 id 或字面坐标;relative `AtPosition` of
|
|
19
|
+
* 必须引用已定义节点/coordinate;解析失败返回 null。
|
|
20
|
+
* - 节点 id lookup 走 NameStack.lookup 进行 inside-out 搜索(内层 frame 可见外层 frame);
|
|
21
|
+
* nodeDistance 为容器 prop 注入默认距离,AtPosition 自带 distance 优先
|
|
6
22
|
*/
|
|
7
|
-
export declare const resolvePosition: (pos: IRPosition | PolarPosition | IRAtPosition | IROffsetPosition | string,
|
|
23
|
+
export declare const resolvePosition: (pos: IRPosition | PolarPosition | IRAtPosition | IROffsetPosition | string, nameStack: NameStack, nodeDistance?: number, scopeChain?: ReadonlyArray<Transform>) => IRPosition | null;
|
|
8
24
|
//# sourceMappingURL=position.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"position.d.ts","sourceRoot":"","sources":["../../../src/compile/position.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACpG,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"position.d.ts","sourceRoot":"","sources":["../../../src/compile/position.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACpG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAqB9C;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,eAAe,GAC1B,KAAK,UAAU,GAAG,aAAa,GAAG,YAAY,GAAG,gBAAgB,GAAG,MAAM,EAC1E,WAAW,SAAS,EACpB,eAAc,MAA8B,EAC5C,aAAY,aAAa,CAAC,SAAS,CAAM,KACxC,UAAU,GAAG,IAyCf,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { inverseTransformChain } from "./scope.js";
|
|
1
2
|
//#region src/compile/position.ts
|
|
2
3
|
/** 默认相对定位距离(CompileOptions.nodeDistance 未配时使用) */
|
|
3
4
|
var DEFAULT_NODE_DISTANCE = 1;
|
|
@@ -17,30 +18,49 @@ var DIRECTION_VECTOR = {
|
|
|
17
18
|
};
|
|
18
19
|
/**
|
|
19
20
|
* IR 各种位置形态(笛卡尔/极坐标/相对定位/偏移定位/节点 id)→ 笛卡尔位置
|
|
20
|
-
* @description
|
|
21
|
+
* @description
|
|
22
|
+
* - **返回值语义**:当 `scopeChain` 非空时,返回**当前 scope 局部坐标系**下的笛卡尔位置;
|
|
23
|
+
* 调用方负责走 `applyTransformChain(local, scopeChain)` 投回全局。当 `scopeChain` 为空时
|
|
24
|
+
* 等价于 v0.1 行为(全局坐标)。
|
|
25
|
+
* - **referent 处理**:node id lookup 拿到的 layout.rect 已是全局坐标;本函数内部用
|
|
26
|
+
* `inverseTransformChain` 把全局 referent 反向投影到当前 scope 局部坐标系作为"在当前
|
|
27
|
+
* scope 局部的固定点"基准。relative 部分(polar 的 angle/radius、at 的 direction/distance、
|
|
28
|
+
* offset 的 dx/dy)在当前 scope 局部度量后加到 referent 局部坐标上。
|
|
29
|
+
* - **嵌套**:PolarPosition.origin / OffsetPosition.of 是嵌套 polar 时,递归调用传同样
|
|
30
|
+
* scopeChain——整条嵌套链都在当前 scope 局部度量。
|
|
31
|
+
* - **笛卡尔字面量**:`Position` 形态直接返回(v0.1 行为延续:scope 内笛卡尔字面量在
|
|
32
|
+
* 当前 scope 局部度量;调用方走 applyTransformChain 投全局)。
|
|
33
|
+
* - 极坐标 origin / 偏移定位 of 均可递归引用节点 id 或字面坐标;relative `AtPosition` of
|
|
34
|
+
* 必须引用已定义节点/coordinate;解析失败返回 null。
|
|
35
|
+
* - 节点 id lookup 走 NameStack.lookup 进行 inside-out 搜索(内层 frame 可见外层 frame);
|
|
36
|
+
* nodeDistance 为容器 prop 注入默认距离,AtPosition 自带 distance 优先
|
|
21
37
|
*/
|
|
22
|
-
var resolvePosition = (pos,
|
|
38
|
+
var resolvePosition = (pos, nameStack, nodeDistance = DEFAULT_NODE_DISTANCE, scopeChain = []) => {
|
|
23
39
|
if (typeof pos === "string") {
|
|
24
|
-
const node =
|
|
25
|
-
|
|
40
|
+
const node = nameStack.lookup(pos);
|
|
41
|
+
if (!node) return null;
|
|
42
|
+
const global = [node.rect.x, node.rect.y];
|
|
43
|
+
return scopeChain.length === 0 ? global : inverseTransformChain(global, scopeChain);
|
|
26
44
|
}
|
|
27
45
|
if (Array.isArray(pos)) return pos;
|
|
28
46
|
if ("direction" in pos) {
|
|
29
|
-
const ref =
|
|
47
|
+
const ref = nameStack.lookup(pos.of);
|
|
30
48
|
if (!ref) return null;
|
|
49
|
+
const refGlobal = [ref.rect.x, ref.rect.y];
|
|
50
|
+
const refLocal = scopeChain.length === 0 ? refGlobal : inverseTransformChain(refGlobal, scopeChain);
|
|
31
51
|
const distance = pos.distance ?? nodeDistance;
|
|
32
52
|
const [dx, dy] = DIRECTION_VECTOR[pos.direction];
|
|
33
|
-
return [
|
|
53
|
+
return [refLocal[0] + dx * distance, refLocal[1] + dy * distance];
|
|
34
54
|
}
|
|
35
55
|
if ("offset" in pos) {
|
|
36
|
-
const base = resolvePosition(pos.of,
|
|
56
|
+
const base = resolvePosition(pos.of, nameStack, nodeDistance, scopeChain);
|
|
37
57
|
if (!base) return null;
|
|
38
58
|
return [base[0] + pos.offset[0], base[1] + pos.offset[1]];
|
|
39
59
|
}
|
|
40
60
|
let origin;
|
|
41
61
|
if (!pos.origin) origin = [0, 0];
|
|
42
62
|
else {
|
|
43
|
-
const resolved = resolvePosition(pos.origin,
|
|
63
|
+
const resolved = resolvePosition(pos.origin, nameStack, nodeDistance, scopeChain);
|
|
44
64
|
if (!resolved) return null;
|
|
45
65
|
origin = resolved;
|
|
46
66
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { IRPosition, IRTransform } from '../ir';
|
|
2
|
+
import { Transform } from '../primitive';
|
|
3
|
+
import { NameStack } from './name-stack';
|
|
4
|
+
import { NodeLayout } from './node';
|
|
5
|
+
/**
|
|
6
|
+
* 把 IR 6 变体 transforms 展平为 Scene 3 变体(Cartesian translate / rotate / scale)
|
|
7
|
+
* @description 4 个 translate 变体(translate / polar-translate / at-translate / offset-translate)
|
|
8
|
+
* 各自构造对应 Position 字面量并调用 `resolvePosition` 拿到 Cartesian (x, y),再写成 Cartesian translate;
|
|
9
|
+
* rotate / scale 直接透传。referent 未解析时返回 null(上游负责发 warn / throw)
|
|
10
|
+
*/
|
|
11
|
+
export declare const lowerScopeTransforms: (transforms: ReadonlyArray<IRTransform>, nameStack: NameStack, nodeDistance?: number) => Array<Transform> | null;
|
|
12
|
+
/**
|
|
13
|
+
* 把局部坐标点 (x, y) 按 Cartesian-only transform 链 apply 到全局坐标
|
|
14
|
+
* @description 数组语义与 SVG `transform="t0 t1 t2"` / TikZ scope option 顺序一致:
|
|
15
|
+
* array[0] 是最外层(最后 apply 到 local),array[last] 是最内层(最先 apply);
|
|
16
|
+
* 即对局部点 P,结果 = t0(t1(t2(P)))。实现上从数组尾部往头部迭代依次 apply。
|
|
17
|
+
* 只接受已被 `lowerScopeTransforms` 展平后的 3 变体(translate / rotate / scale)
|
|
18
|
+
*/
|
|
19
|
+
export declare const applyTransformChain: (local: IRPosition, chain: ReadonlyArray<Transform>) => IRPosition;
|
|
20
|
+
/**
|
|
21
|
+
* 把全局坐标点反向投影回 scope 局部坐标系(`applyTransformChain` 的逆)
|
|
22
|
+
* @description chain = [t0, t1, t2](array[0] 最外层、最后 apply)的逆 =
|
|
23
|
+
* 按数组正序应用每个 transform 的逆:t0^-1 / t1^-1 / t2^-1。
|
|
24
|
+
* translate(-x, -y) / rotate(-deg, cx, cy) / scale(1/x, 1/y)。
|
|
25
|
+
* scale 分量为 0 时反向投影未定义——退化为返回原点 (0, 0) 当前层,避免 NaN 污染下游。
|
|
26
|
+
* 作用:referent 全局点 → 当前 scope 局部坐标系,配合 `applyTransformChain` 实现
|
|
27
|
+
* "referent 全局 + relative 部分在当前 scope 局部度量 + 末端正向投影回全局" 的语义。
|
|
28
|
+
*/
|
|
29
|
+
export declare const inverseTransformChain: (global: IRPosition, chain: ReadonlyArray<Transform>) => IRPosition;
|
|
30
|
+
/**
|
|
31
|
+
* 复制 NodeLayout 并把 rect 中心点 + rotate + 尺寸全部按 scope transform chain 投到全局
|
|
32
|
+
* @description rect 中心走 `applyTransformChain`;chain 里的 rotate 累加到 `rect.rotate`(弧度)、
|
|
33
|
+
* scale 乘进 rect.width / height / margin——这样 path 端点的 boundary clip 取的是与 SVG `<g>`
|
|
34
|
+
* 实际渲染一致的视觉尺寸 / 朝向,跨 / 入 / 出 rotate / scale scope 的 path 都贴节点视觉边界。
|
|
35
|
+
* 非均匀 scale 与 rotate 在 chain 中混合时,按"累加 rotate + 分量相乘 scale"近似(uniform scale 精确,
|
|
36
|
+
* anisotropic + rotate 的剪切耦合不展开——alpha 阶段限制)。
|
|
37
|
+
*/
|
|
38
|
+
export declare const projectLayoutToGlobal: (layout: NodeLayout, chain: ReadonlyArray<Transform>) => NodeLayout;
|
|
39
|
+
/** scope bbox 计算结果:bbox 几何中心 + 尺寸(width/height ≥ 0;空 scope 退化为 0×0 占位时仍合法) */
|
|
40
|
+
export type ScopeBoundingBox = {
|
|
41
|
+
/** bbox 几何中心 x(全局坐标) */
|
|
42
|
+
x: number;
|
|
43
|
+
/** bbox 几何中心 y(全局坐标) */
|
|
44
|
+
y: number;
|
|
45
|
+
/** bbox 宽度(≥ 0;空 scope / 单点退化为 0) */
|
|
46
|
+
width: number;
|
|
47
|
+
/** bbox 高度(≥ 0;空 scope / 单点退化为 0) */
|
|
48
|
+
height: number;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* 收集一组 NodeLayout 的全局 axis-aligned bounding box
|
|
52
|
+
* @description 每个 layout 的 4 角点已是全局坐标系(Pass 1 累积 chain apply 后),
|
|
53
|
+
* 取每个 layout 的 rotate-aware `north-west` / `north-east` / `south-west` / `south-east`
|
|
54
|
+
* 4 角点(rect.anchor 已含 layout.rect.rotate 处理)并求 AABB;
|
|
55
|
+
* layout 是 0×0(coordinate / 空 scope 占位)时退化为单点也合法;
|
|
56
|
+
* 空 layouts 数组返回 null(调用方按"empty scope + fallback origin"退化为 0×0 占位)
|
|
57
|
+
*/
|
|
58
|
+
export declare const computeScopeBoundingBox: (layouts: ReadonlyArray<NodeLayout>) => ScopeBoundingBox | null;
|
|
59
|
+
/**
|
|
60
|
+
* 用 scope id + bbox 构造 synthetic rectangle NodeLayout
|
|
61
|
+
* @description bbox 为 null 时退化为 fallbackOrigin 的 0×0 占位(空 scope 仍要有可引用句柄)。
|
|
62
|
+
* synthetic layout 完全复用 rectangle 路径:`scope.id.<keyword>` / `scope.id.<deg>` / `scope.id` 作为 referent
|
|
63
|
+
* 走与普通 rectangle Node 完全一致的 anchorOf / boundaryPointOf / 中心点取值
|
|
64
|
+
*/
|
|
65
|
+
export declare const registerScopeAsLayout: (id: string, bbox: ScopeBoundingBox | null, fallbackOrigin: IRPosition) => NodeLayout;
|
|
66
|
+
//# sourceMappingURL=scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../../src/compile/scope.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,UAAU,EACV,WAAW,EAEZ,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAGzC;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAC/B,YAAY,aAAa,CAAC,WAAW,CAAC,EACtC,WAAW,SAAS,EACpB,eAAe,MAAM,KACpB,KAAK,CAAC,SAAS,CAAC,GAAG,IAiDrB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAC9B,OAAO,UAAU,EACjB,OAAO,aAAa,CAAC,SAAS,CAAC,KAC9B,UAyBF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,GAChC,QAAQ,UAAU,EAClB,OAAO,aAAa,CAAC,SAAS,CAAC,KAC9B,UAgCF,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,GAChC,QAAQ,UAAU,EAClB,OAAO,aAAa,CAAC,SAAS,CAAC,KAC9B,UAuBF,CAAC;AAEF,8EAA8E;AAC9E,MAAM,MAAM,gBAAgB,GAAG;IAC7B,wBAAwB;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,wBAAwB;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,GAClC,SAAS,aAAa,CAAC,UAAU,CAAC,KACjC,gBAAgB,GAAG,IA2BrB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAChC,IAAI,MAAM,EACV,MAAM,gBAAgB,GAAG,IAAI,EAC7B,gBAAgB,UAAU,KACzB,UAeF,CAAC"}
|