@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.
Files changed (117) hide show
  1. package/dist/es/compile/anchor-cache.d.ts +12 -0
  2. package/dist/es/compile/anchor-cache.d.ts.map +1 -0
  3. package/dist/es/compile/anchor-cache.js +41 -0
  4. package/dist/es/compile/compile.d.ts +2 -2
  5. package/dist/es/compile/compile.d.ts.map +1 -1
  6. package/dist/es/compile/compile.js +150 -40
  7. package/dist/es/compile/index.d.ts +1 -0
  8. package/dist/es/compile/index.d.ts.map +1 -1
  9. package/dist/es/compile/name-stack.d.ts +81 -0
  10. package/dist/es/compile/name-stack.d.ts.map +1 -0
  11. package/dist/es/compile/name-stack.js +104 -0
  12. package/dist/es/compile/node.d.ts +9 -5
  13. package/dist/es/compile/node.d.ts.map +1 -1
  14. package/dist/es/compile/node.js +19 -11
  15. package/dist/es/compile/path/anchor.d.ts +11 -5
  16. package/dist/es/compile/path/anchor.d.ts.map +1 -1
  17. package/dist/es/compile/path/anchor.js +24 -13
  18. package/dist/es/compile/path/index.d.ts +9 -3
  19. package/dist/es/compile/path/index.d.ts.map +1 -1
  20. package/dist/es/compile/path/index.js +18 -17
  21. package/dist/es/compile/path/label.d.ts +1 -1
  22. package/dist/es/compile/path/label.d.ts.map +1 -1
  23. package/dist/es/compile/path/label.js +17 -4
  24. package/dist/es/compile/path/relative.d.ts +10 -4
  25. package/dist/es/compile/path/relative.d.ts.map +1 -1
  26. package/dist/es/compile/path/relative.js +16 -8
  27. package/dist/es/compile/position.d.ts +19 -3
  28. package/dist/es/compile/position.d.ts.map +1 -1
  29. package/dist/es/compile/position.js +28 -8
  30. package/dist/es/compile/scope.d.ts +66 -0
  31. package/dist/es/compile/scope.d.ts.map +1 -0
  32. package/dist/es/compile/scope.js +256 -0
  33. package/dist/es/compile/style.d.ts +46 -0
  34. package/dist/es/compile/style.d.ts.map +1 -0
  35. package/dist/es/compile/style.js +259 -0
  36. package/dist/es/index.d.ts +2 -2
  37. package/dist/es/index.d.ts.map +1 -1
  38. package/dist/es/index.js +3 -1
  39. package/dist/es/ir/index.d.ts +2 -0
  40. package/dist/es/ir/index.d.ts.map +1 -1
  41. package/dist/es/ir/node.d.ts +5 -2
  42. package/dist/es/ir/node.d.ts.map +1 -1
  43. package/dist/es/ir/node.js +1 -0
  44. package/dist/es/ir/path/path.d.ts +531 -0
  45. package/dist/es/ir/path/path.d.ts.map +1 -1
  46. package/dist/es/ir/path/path.js +1 -0
  47. package/dist/es/ir/path/step.d.ts +834 -0
  48. package/dist/es/ir/path/step.d.ts.map +1 -1
  49. package/dist/es/ir/path/step.js +5 -1
  50. package/dist/es/ir/scene.d.ts +18 -3542
  51. package/dist/es/ir/scene.d.ts.map +1 -1
  52. package/dist/es/ir/scene.js +11 -3
  53. package/dist/es/ir/scope.d.ts +3690 -0
  54. package/dist/es/ir/scope.d.ts.map +1 -0
  55. package/dist/es/ir/scope.js +89 -0
  56. package/dist/es/ir/transform.d.ts +204 -0
  57. package/dist/es/ir/transform.d.ts.map +1 -0
  58. package/dist/es/ir/transform.js +56 -0
  59. package/dist/lib/compile/anchor-cache.cjs +41 -0
  60. package/dist/lib/compile/anchor-cache.d.ts +12 -0
  61. package/dist/lib/compile/anchor-cache.d.ts.map +1 -0
  62. package/dist/lib/compile/compile.cjs +150 -40
  63. package/dist/lib/compile/compile.d.ts +2 -2
  64. package/dist/lib/compile/compile.d.ts.map +1 -1
  65. package/dist/lib/compile/index.d.ts +1 -0
  66. package/dist/lib/compile/index.d.ts.map +1 -1
  67. package/dist/lib/compile/name-stack.cjs +104 -0
  68. package/dist/lib/compile/name-stack.d.ts +81 -0
  69. package/dist/lib/compile/name-stack.d.ts.map +1 -0
  70. package/dist/lib/compile/node.cjs +19 -11
  71. package/dist/lib/compile/node.d.ts +9 -5
  72. package/dist/lib/compile/node.d.ts.map +1 -1
  73. package/dist/lib/compile/path/anchor.cjs +23 -12
  74. package/dist/lib/compile/path/anchor.d.ts +11 -5
  75. package/dist/lib/compile/path/anchor.d.ts.map +1 -1
  76. package/dist/lib/compile/path/index.cjs +18 -17
  77. package/dist/lib/compile/path/index.d.ts +9 -3
  78. package/dist/lib/compile/path/index.d.ts.map +1 -1
  79. package/dist/lib/compile/path/label.cjs +17 -4
  80. package/dist/lib/compile/path/label.d.ts +1 -1
  81. package/dist/lib/compile/path/label.d.ts.map +1 -1
  82. package/dist/lib/compile/path/relative.cjs +16 -8
  83. package/dist/lib/compile/path/relative.d.ts +10 -4
  84. package/dist/lib/compile/path/relative.d.ts.map +1 -1
  85. package/dist/lib/compile/position.cjs +28 -8
  86. package/dist/lib/compile/position.d.ts +19 -3
  87. package/dist/lib/compile/position.d.ts.map +1 -1
  88. package/dist/lib/compile/scope.cjs +261 -0
  89. package/dist/lib/compile/scope.d.ts +66 -0
  90. package/dist/lib/compile/scope.d.ts.map +1 -0
  91. package/dist/lib/compile/style.cjs +262 -0
  92. package/dist/lib/compile/style.d.ts +46 -0
  93. package/dist/lib/compile/style.d.ts.map +1 -0
  94. package/dist/lib/index.cjs +8 -0
  95. package/dist/lib/index.d.ts +2 -2
  96. package/dist/lib/index.d.ts.map +1 -1
  97. package/dist/lib/ir/index.d.ts +2 -0
  98. package/dist/lib/ir/index.d.ts.map +1 -1
  99. package/dist/lib/ir/node.cjs +1 -0
  100. package/dist/lib/ir/node.d.ts +5 -2
  101. package/dist/lib/ir/node.d.ts.map +1 -1
  102. package/dist/lib/ir/path/path.cjs +1 -0
  103. package/dist/lib/ir/path/path.d.ts +531 -0
  104. package/dist/lib/ir/path/path.d.ts.map +1 -1
  105. package/dist/lib/ir/path/step.cjs +5 -1
  106. package/dist/lib/ir/path/step.d.ts +834 -0
  107. package/dist/lib/ir/path/step.d.ts.map +1 -1
  108. package/dist/lib/ir/scene.cjs +11 -3
  109. package/dist/lib/ir/scene.d.ts +18 -3542
  110. package/dist/lib/ir/scene.d.ts.map +1 -1
  111. package/dist/lib/ir/scope.cjs +94 -0
  112. package/dist/lib/ir/scope.d.ts +3690 -0
  113. package/dist/lib/ir/scope.d.ts.map +1 -0
  114. package/dist/lib/ir/transform.cjs +56 -0
  115. package/dist/lib/ir/transform.d.ts +204 -0
  116. package/dist/lib/ir/transform.d.ts.map +1 -0
  117. package/package.json +1 -1
@@ -1,44 +1,55 @@
1
+ const require_scope = require("../scope.cjs");
1
2
  const require_position = require("../position.cjs");
2
3
  const require_node = require("../node.cjs");
4
+ const require_anchor_cache = require("../anchor-cache.cjs");
3
5
  const require_parseTarget = require("../parseTarget.cjs");
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, nodeIndex) => {
14
+ var refPointOfTarget = (target, nameStack, scopeChain = []) => {
10
15
  if (typeof target === "string") {
11
16
  const ref = require_parseTarget.parseNodeRef(target);
12
- const node = nodeIndex.get(ref.id);
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 require_node.anchorOf(node, ref.anchor);
17
- case "angle": return require_node.angleBoundaryOf(node, ref.angle);
21
+ case "anchor": return require_anchor_cache.resolveAnchor(node, ref.anchor);
22
+ case "angle": return require_anchor_cache.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
- return require_position.resolvePosition(target, nodeIndex);
26
+ const local = require_position.resolvePosition(target, nameStack, void 0, scopeChain);
27
+ if (!local) return null;
28
+ return scopeChain.length === 0 ? local : require_scope.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, nodeIndex) => {
38
+ var clipForTarget = (target, toward, nameStack, scopeChain = []) => {
30
39
  if (typeof target === "string") {
31
40
  const ref = require_parseTarget.parseNodeRef(target);
32
- const node = nodeIndex.get(ref.id);
41
+ const node = nameStack.lookup(ref.id);
33
42
  if (!node) return null;
34
43
  switch (ref.kind) {
35
44
  case "node": return require_node.boundaryPointOf(node, toward);
36
- case "anchor": return require_node.anchorOf(node, ref.anchor);
37
- case "angle": return require_node.angleBoundaryOf(node, ref.angle);
45
+ case "anchor": return require_anchor_cache.resolveAnchor(node, ref.anchor);
46
+ case "angle": return require_anchor_cache.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
- return require_position.resolvePosition(target, nodeIndex);
50
+ const local = require_position.resolvePosition(target, nameStack, void 0, scopeChain);
51
+ if (!local) return null;
52
+ return scopeChain.length === 0 ? local : require_scope.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,17 +1,23 @@
1
1
  import { IRPosition, IRTarget } from '../../ir';
2
- import { NodeLayout } from '../node';
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, nodeIndex: Map<string, NodeLayout>) => IRPosition | null;
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, nodeIndex: Map<string, NodeLayout>) => IRPosition | null;
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,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"}
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"}
@@ -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, nodeIndex, round, measureText = require_text_metrics.fallbackMeasurer, warnHook = {}) => {
29
+ var emitPathPrimitive = (path, nameStack, round, measureText = require_text_metrics.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 = require_text_metr
35
35
  path: subPath ? `${irPath}.${subPath}` : irPath
36
36
  });
37
37
  };
38
- const steps = require_relative.normalizeRelativeTargets(path.children, nodeIndex);
38
+ const scopeChain = warnHook.scopeChain ?? [];
39
+ const steps = require_relative.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 = require_text_metr
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(require_label.tForLabelPosition(step.label.position));
49
- const r = require_label.emitLabelPrimitive(step.label, sample, measureText, round);
50
+ const r = require_label.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 = require_anchor.refPointOfTarget(s.to, nodeIndex);
57
+ const ref = require_anchor.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 = require_text_metr
179
180
  const moveTo = lastMoveTo;
180
181
  const prev = findPrev();
181
182
  if (!moveTo || !prev) continue;
182
- const moveAnchor = require_anchor.refPointOfTarget(moveTo, nodeIndex);
183
+ const moveAnchor = require_anchor.refPointOfTarget(moveTo, nameStack, scopeChain);
183
184
  if (!moveAnchor) return null;
184
- const fromClip = require_anchor.clipForTarget(prev.step.to, moveAnchor, nodeIndex);
185
- const toClip = require_anchor.clipForTarget(moveTo, prev.anchor, nodeIndex);
185
+ const fromClip = require_anchor.clipForTarget(prev.step.to, moveAnchor, nameStack, scopeChain);
186
+ const toClip = require_anchor.clipForTarget(moveTo, prev.anchor, nameStack, scopeChain);
186
187
  if (!fromClip || !toClip) return null;
187
188
  if (require_anchor.samePoint(fromClip, lastEnd) && require_anchor.samePoint(toClip, subPathStart)) {
188
189
  emitClose();
@@ -237,8 +238,8 @@ var emitPathPrimitive = (path, nodeIndex, round, measureText = require_text_metr
237
238
  const usedOverride = penOverride;
238
239
  penOverride = null;
239
240
  if (step.kind === "line") {
240
- const fromClip = usedOverride ?? require_anchor.clipForTarget(prev.step.to, currAnchor, nodeIndex);
241
- const toClip = require_anchor.clipForTarget(step.to, prev.anchor, nodeIndex);
241
+ const fromClip = usedOverride ?? require_anchor.clipForTarget(prev.step.to, currAnchor, nameStack, scopeChain);
242
+ const toClip = require_anchor.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 = require_text_metr
246
247
  continue;
247
248
  }
248
249
  if (step.kind === "curve") {
249
- const fromClip = usedOverride ?? require_anchor.clipForTarget(prev.step.to, step.control, nodeIndex);
250
- const toClip = require_anchor.clipForTarget(step.to, step.control, nodeIndex);
250
+ const fromClip = usedOverride ?? require_anchor.clipForTarget(prev.step.to, step.control, nameStack, scopeChain);
251
+ const toClip = require_anchor.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 = require_text_metr
255
256
  continue;
256
257
  }
257
258
  if (step.kind === "cubic") {
258
- const fromClip = usedOverride ?? require_anchor.clipForTarget(prev.step.to, step.control1, nodeIndex);
259
- const toClip = require_anchor.clipForTarget(step.to, step.control2, nodeIndex);
259
+ const fromClip = usedOverride ?? require_anchor.clipForTarget(prev.step.to, step.control1, nameStack, scopeChain);
260
+ const toClip = require_anchor.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 = require_text_metr
266
267
  if (step.kind === "bend") {
267
268
  const angle = step.bendAngle ?? 30;
268
269
  const [c1, c2] = require_bend.bendControlPoints(prev.anchor, currAnchor, step.bendDirection, angle);
269
- const fromClip = usedOverride ?? require_anchor.clipForTarget(prev.step.to, c1, nodeIndex);
270
- const toClip = require_anchor.clipForTarget(step.to, c2, nodeIndex);
270
+ const fromClip = usedOverride ?? require_anchor.clipForTarget(prev.step.to, c1, nameStack, scopeChain);
271
+ const toClip = require_anchor.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 = require_text_metr
275
276
  continue;
276
277
  }
277
278
  const corner = require_anchor.cornerOf(prev.anchor, currAnchor, step.via);
278
- const fromClip = usedOverride ?? require_anchor.clipForTarget(prev.step.to, corner, nodeIndex);
279
- const toClip = require_anchor.clipForTarget(step.to, corner, nodeIndex);
279
+ const fromClip = usedOverride ?? require_anchor.clipForTarget(prev.step.to, corner, nameStack, scopeChain);
280
+ const toClip = require_anchor.clipForTarget(step.to, corner, nameStack, scopeChain);
280
281
  if (!fromClip || !toClip) return null;
281
282
  startSegment(fromClip);
282
283
  emitLine(corner);
@@ -1,6 +1,6 @@
1
1
  import { IRPath, IRPosition } from '../../ir';
2
- import { ScenePrimitive } from '../../primitive';
3
- import { NodeLayout } from '../node';
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, nodeIndex: Map<string, NodeLayout>, round: (n: number) => number, measureText?: TextMeasurer, warnHook?: EmitPathWarnHook) => {
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,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"}
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"}
@@ -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, { size: fontSize });
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",
@@ -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,KAC3B;IAAE,SAAS,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAA;CA8ExD,CAAC"}
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"}
@@ -1,11 +1,17 @@
1
+ const require_scope = require("../scope.cjs");
1
2
  const require_arc = require("../../geometry/arc.cjs");
2
3
  const require_anchor = require("./anchor.cjs");
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, nodeIndex) => {
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 ref = prevEnd ?? [0, 0];
30
- resolvedTo = [ref[0] + original.relative[0], ref[1] + original.relative[1]];
35
+ const refGlobal = prevEnd ?? [0, 0];
36
+ const refLocal = scopeChain.length === 0 ? refGlobal : require_scope.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 ref = prevEnd ?? [0, 0];
34
- resolvedTo = [ref[0] + original.relativeAccumulate[0], ref[1] + original.relativeAccumulate[1]];
40
+ const refGlobal = prevEnd ?? [0, 0];
41
+ const refLocal = scopeChain.length === 0 ? refGlobal : require_scope.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 = require_anchor.refPointOfTarget(resolvedTo, nodeIndex);
49
+ const pos = require_anchor.refPointOfTarget(resolvedTo, nameStack, scopeChain);
42
50
  if (pos) prevEnd = pos;
43
51
  }
44
52
  }
@@ -1,8 +1,14 @@
1
1
  import { IRStep } from '../../ir';
2
- import { NodeLayout } from '../node';
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>, nodeIndex: Map<string, NodeLayout>) => Array<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,UAAU,EAAE,MAAM,SAAS,CAAC;AAG1C;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GACnC,OAAO,aAAa,CAAC,MAAM,CAAC,EAC5B,WAAW,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,KACjC,KAAK,CAAC,MAAM,CA0Dd,CAAC"}
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,3 +1,4 @@
1
+ const require_scope = require("./scope.cjs");
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 极坐标 origin / 偏移定位 of 均可递归引用节点 id 或字面坐标;relative `AtPosition` of 必须引用已定义节点/coordinate;解析失败返回 null。nodeDistance 为容器 prop 注入默认距离,AtPosition 自带 distance 优先
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, nodeMap, nodeDistance = DEFAULT_NODE_DISTANCE) => {
38
+ var resolvePosition = (pos, nameStack, nodeDistance = DEFAULT_NODE_DISTANCE, scopeChain = []) => {
23
39
  if (typeof pos === "string") {
24
- const node = nodeMap.get(pos);
25
- return node ? [node.rect.x, node.rect.y] : null;
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 : require_scope.inverseTransformChain(global, scopeChain);
26
44
  }
27
45
  if (Array.isArray(pos)) return pos;
28
46
  if ("direction" in pos) {
29
- const ref = nodeMap.get(pos.of);
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 : require_scope.inverseTransformChain(refGlobal, scopeChain);
31
51
  const distance = pos.distance ?? nodeDistance;
32
52
  const [dx, dy] = DIRECTION_VECTOR[pos.direction];
33
- return [ref.rect.x + dx * distance, ref.rect.y + dy * distance];
53
+ return [refLocal[0] + dx * distance, refLocal[1] + dy * distance];
34
54
  }
35
55
  if ("offset" in pos) {
36
- const base = resolvePosition(pos.of, nodeMap, nodeDistance);
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, nodeMap, nodeDistance);
63
+ const resolved = resolvePosition(pos.origin, nameStack, nodeDistance, scopeChain);
44
64
  if (!resolved) return null;
45
65
  origin = resolved;
46
66
  }
@@ -1,8 +1,24 @@
1
1
  import { IRAtPosition, IROffsetPosition, IRPosition, PolarPosition } from '../ir';
2
- import { NodeLayout } from './node';
2
+ import { Transform } from '../primitive';
3
+ import { NameStack } from './name-stack';
3
4
  /**
4
5
  * IR 各种位置形态(笛卡尔/极坐标/相对定位/偏移定位/节点 id)→ 笛卡尔位置
5
- * @description 极坐标 origin / 偏移定位 of 均可递归引用节点 id 或字面坐标;relative `AtPosition` of 必须引用已定义节点/coordinate;解析失败返回 null。nodeDistance 为容器 prop 注入默认距离,AtPosition 自带 distance 优先
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, nodeMap: Map<string, NodeLayout>, nodeDistance?: number) => IRPosition | null;
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,UAAU,EAAE,MAAM,QAAQ,CAAC;AAoBzC;;;GAGG;AACH,eAAO,MAAM,eAAe,GAC1B,KAAK,UAAU,GAAG,aAAa,GAAG,YAAY,GAAG,gBAAgB,GAAG,MAAM,EAC1E,SAAS,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EAChC,eAAc,MAA8B,KAC3C,UAAU,GAAG,IAkCf,CAAC"}
1
+ {"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"}