@retikz/core 0.2.0-alpha.7 → 0.2.0-alpha.8
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/arrows/index.d.ts +13 -0
- package/dist/es/arrows/index.d.ts.map +1 -0
- package/dist/es/arrows/index.js +118 -0
- package/dist/es/arrows/types.d.ts +43 -0
- package/dist/es/arrows/types.d.ts.map +1 -0
- package/dist/es/compile/compile.d.ts +26 -1
- package/dist/es/compile/compile.d.ts.map +1 -1
- package/dist/es/compile/compile.js +29 -2
- package/dist/es/compile/marker-prim.d.ts +22 -0
- package/dist/es/compile/marker-prim.d.ts.map +1 -0
- package/dist/es/compile/marker-prim.js +63 -0
- package/dist/es/compile/paint.d.ts +6 -1
- package/dist/es/compile/paint.d.ts.map +1 -1
- package/dist/es/compile/paint.js +60 -3
- package/dist/es/compile/path/index.d.ts +15 -0
- package/dist/es/compile/path/index.d.ts.map +1 -1
- package/dist/es/compile/path/index.js +258 -10
- package/dist/es/compile/path/relative.d.ts.map +1 -1
- package/dist/es/compile/path/relative.js +8 -0
- package/dist/es/compile/path/shrink.d.ts +19 -6
- package/dist/es/compile/path/shrink.d.ts.map +1 -1
- package/dist/es/compile/path/shrink.js +147 -25
- package/dist/es/geometry/bend.d.ts +7 -0
- package/dist/es/geometry/bend.d.ts.map +1 -1
- package/dist/es/geometry/bend.js +26 -1
- package/dist/es/index.d.ts +14 -4
- package/dist/es/index.d.ts.map +1 -1
- package/dist/es/index.js +7 -3
- package/dist/es/ir/index.d.ts +1 -0
- package/dist/es/ir/index.d.ts.map +1 -1
- package/dist/es/ir/json.d.ts +22 -0
- package/dist/es/ir/json.d.ts.map +1 -0
- package/dist/es/ir/json.js +25 -0
- package/dist/es/ir/node.d.ts +5 -5
- package/dist/es/ir/paint.d.ts +25 -3
- package/dist/es/ir/paint.d.ts.map +1 -1
- package/dist/es/ir/paint.js +12 -6
- package/dist/es/ir/path/arrow.d.ts +28 -48
- package/dist/es/ir/path/arrow.d.ts.map +1 -1
- package/dist/es/ir/path/arrow.js +5 -5
- package/dist/es/ir/path/path.d.ts +444 -55
- package/dist/es/ir/path/path.d.ts.map +1 -1
- package/dist/es/ir/path/path.js +21 -1
- package/dist/es/ir/path/step.d.ts +363 -8
- package/dist/es/ir/path/step.d.ts.map +1 -1
- package/dist/es/ir/path/step.js +17 -4
- package/dist/es/ir/scope.d.ts +703 -189
- package/dist/es/ir/scope.d.ts.map +1 -1
- package/dist/es/pathGenerators/define.d.ts +16 -0
- package/dist/es/pathGenerators/define.d.ts.map +1 -0
- package/dist/es/pathGenerators/define.js +23 -0
- package/dist/es/pathGenerators/index.d.ts +9 -0
- package/dist/es/pathGenerators/index.d.ts.map +1 -0
- package/dist/es/pathGenerators/types.d.ts +45 -0
- package/dist/es/pathGenerators/types.d.ts.map +1 -0
- package/dist/es/patterns/index.d.ts +10 -0
- package/dist/es/patterns/index.d.ts.map +1 -0
- package/dist/es/patterns/index.js +83 -0
- package/dist/es/patterns/types.d.ts +38 -0
- package/dist/es/patterns/types.d.ts.map +1 -0
- package/dist/es/primitive/index.d.ts +1 -0
- package/dist/es/primitive/index.d.ts.map +1 -1
- package/dist/es/primitive/marker.d.ts +160 -0
- package/dist/es/primitive/marker.d.ts.map +1 -0
- package/dist/es/primitive/paint.d.ts +24 -2
- package/dist/es/primitive/paint.d.ts.map +1 -1
- package/dist/es/primitive/path.d.ts +22 -18
- package/dist/es/primitive/path.d.ts.map +1 -1
- package/dist/es/primitive/scene.d.ts +1 -1
- package/dist/es/primitive/scene.d.ts.map +1 -1
- package/dist/lib/arrows/index.cjs +118 -0
- package/dist/lib/arrows/index.d.ts +13 -0
- package/dist/lib/arrows/index.d.ts.map +1 -0
- package/dist/lib/arrows/types.d.ts +43 -0
- package/dist/lib/arrows/types.d.ts.map +1 -0
- package/dist/lib/compile/compile.cjs +31 -4
- package/dist/lib/compile/compile.d.ts +26 -1
- package/dist/lib/compile/compile.d.ts.map +1 -1
- package/dist/lib/compile/marker-prim.cjs +63 -0
- package/dist/lib/compile/marker-prim.d.ts +22 -0
- package/dist/lib/compile/marker-prim.d.ts.map +1 -0
- package/dist/lib/compile/paint.cjs +60 -3
- package/dist/lib/compile/paint.d.ts +6 -1
- package/dist/lib/compile/paint.d.ts.map +1 -1
- package/dist/lib/compile/path/index.cjs +256 -8
- package/dist/lib/compile/path/index.d.ts +15 -0
- package/dist/lib/compile/path/index.d.ts.map +1 -1
- package/dist/lib/compile/path/relative.cjs +8 -0
- package/dist/lib/compile/path/relative.d.ts.map +1 -1
- package/dist/lib/compile/path/shrink.cjs +147 -25
- package/dist/lib/compile/path/shrink.d.ts +19 -6
- package/dist/lib/compile/path/shrink.d.ts.map +1 -1
- package/dist/lib/geometry/bend.cjs +26 -0
- package/dist/lib/geometry/bend.d.ts +7 -0
- package/dist/lib/geometry/bend.d.ts.map +1 -1
- package/dist/lib/index.cjs +11 -0
- package/dist/lib/index.d.ts +14 -4
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/ir/index.d.ts +1 -0
- package/dist/lib/ir/index.d.ts.map +1 -1
- package/dist/lib/ir/json.cjs +26 -0
- package/dist/lib/ir/json.d.ts +22 -0
- package/dist/lib/ir/json.d.ts.map +1 -0
- package/dist/lib/ir/node.d.ts +5 -5
- package/dist/lib/ir/paint.cjs +12 -5
- package/dist/lib/ir/paint.d.ts +25 -3
- package/dist/lib/ir/paint.d.ts.map +1 -1
- package/dist/lib/ir/path/arrow.cjs +5 -5
- package/dist/lib/ir/path/arrow.d.ts +28 -48
- package/dist/lib/ir/path/arrow.d.ts.map +1 -1
- package/dist/lib/ir/path/path.cjs +20 -0
- package/dist/lib/ir/path/path.d.ts +444 -55
- package/dist/lib/ir/path/path.d.ts.map +1 -1
- package/dist/lib/ir/path/step.cjs +17 -3
- package/dist/lib/ir/path/step.d.ts +363 -8
- package/dist/lib/ir/path/step.d.ts.map +1 -1
- package/dist/lib/ir/scope.d.ts +703 -189
- package/dist/lib/ir/scope.d.ts.map +1 -1
- package/dist/lib/pathGenerators/define.cjs +23 -0
- package/dist/lib/pathGenerators/define.d.ts +16 -0
- package/dist/lib/pathGenerators/define.d.ts.map +1 -0
- package/dist/lib/pathGenerators/index.d.ts +9 -0
- package/dist/lib/pathGenerators/index.d.ts.map +1 -0
- package/dist/lib/pathGenerators/types.d.ts +45 -0
- package/dist/lib/pathGenerators/types.d.ts.map +1 -0
- package/dist/lib/patterns/index.cjs +83 -0
- package/dist/lib/patterns/index.d.ts +10 -0
- package/dist/lib/patterns/index.d.ts.map +1 -0
- package/dist/lib/patterns/types.d.ts +38 -0
- package/dist/lib/patterns/types.d.ts.map +1 -0
- package/dist/lib/primitive/index.d.ts +1 -0
- package/dist/lib/primitive/index.d.ts.map +1 -1
- package/dist/lib/primitive/marker.d.ts +160 -0
- package/dist/lib/primitive/marker.d.ts.map +1 -0
- package/dist/lib/primitive/paint.d.ts +24 -2
- package/dist/lib/primitive/paint.d.ts.map +1 -1
- package/dist/lib/primitive/path.d.ts +22 -18
- package/dist/lib/primitive/path.d.ts.map +1 -1
- package/dist/lib/primitive/scene.d.ts +1 -1
- package/dist/lib/primitive/scene.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/es/compile/path/arrow-geometry.d.ts +0 -22
- package/dist/es/compile/path/arrow-geometry.d.ts.map +0 -1
- package/dist/es/compile/path/arrow-geometry.js +0 -40
- package/dist/lib/compile/path/arrow-geometry.cjs +0 -41
- package/dist/lib/compile/path/arrow-geometry.d.ts +0 -22
- package/dist/lib/compile/path/arrow-geometry.d.ts.map +0 -1
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
const require_json = require("../../ir/json.cjs");
|
|
1
2
|
const require_rect = require("../../geometry/rect.cjs");
|
|
3
|
+
const require_index = require("../../arrows/index.cjs");
|
|
4
|
+
const require_scope = require("../scope.cjs");
|
|
2
5
|
const require_arc = require("../../geometry/arc.cjs");
|
|
3
6
|
const require_bend = require("../../geometry/bend.cjs");
|
|
4
7
|
const require_segment = require("../../geometry/segment.cjs");
|
|
@@ -11,6 +14,46 @@ const require_split = require("./split.cjs");
|
|
|
11
14
|
//#region src/compile/path/index.ts
|
|
12
15
|
/** 目标若是对象 NodeTarget(`{ id, ... }`)返回其 id,否则 undefined——给 UNRESOLVED_NODE_REFERENCE 诊断用 */
|
|
13
16
|
var nodeRefId = (t) => typeof t === "object" && !Array.isArray(t) && "id" in t ? t.id : void 0;
|
|
17
|
+
/** 有限数 */
|
|
18
|
+
var isFiniteNum = (n) => typeof n === "number" && Number.isFinite(n);
|
|
19
|
+
/** 有限坐标点 `[number, number]` */
|
|
20
|
+
var isFinitePoint = (pt) => Array.isArray(pt) && pt.length >= 2 && isFiniteNum(pt[0]) && isFiniteNum(pt[1]);
|
|
21
|
+
/**
|
|
22
|
+
* 校验 path generator 产出的单条命令合法(kind 已知 + 引用坐标 / 数值有限)
|
|
23
|
+
* @description 第三方 / LLM 写的 generate 误产坏命令(NaN/Infinity 坐标、未知 kind、缺字段、字符串当命令)时
|
|
24
|
+
* 抛含 generator 名的清晰错——守 Scene 100% finite / JSON 可序列化,不放任非 finite 静默入 Scene
|
|
25
|
+
* (JSON.stringify(NaN/Infinity)=null 会让 round-trip 失真)。
|
|
26
|
+
*/
|
|
27
|
+
var assertValidGeneratedCommand = (name, cmd) => {
|
|
28
|
+
const bad = (detail) => {
|
|
29
|
+
throw new Error(`path generator '${name}' produced a ${detail}.`);
|
|
30
|
+
};
|
|
31
|
+
if (cmd === null || typeof cmd !== "object") {
|
|
32
|
+
bad(`non-object command (expected an object with a 'kind')`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const c = cmd;
|
|
36
|
+
switch (c.kind) {
|
|
37
|
+
case "move":
|
|
38
|
+
case "line":
|
|
39
|
+
if (!isFinitePoint(c.to)) bad(`non-finite coordinate in a '${String(c.kind)}' command`);
|
|
40
|
+
break;
|
|
41
|
+
case "quad":
|
|
42
|
+
if (!isFinitePoint(c.control) || !isFinitePoint(c.to)) bad(`non-finite coordinate in a 'quad' command`);
|
|
43
|
+
break;
|
|
44
|
+
case "cubic":
|
|
45
|
+
if (!isFinitePoint(c.control1) || !isFinitePoint(c.control2) || !isFinitePoint(c.to)) bad(`non-finite coordinate in a 'cubic' command`);
|
|
46
|
+
break;
|
|
47
|
+
case "arc":
|
|
48
|
+
if (!isFinitePoint(c.center) || !isFiniteNum(c.radius) || !isFiniteNum(c.startAngle) || !isFiniteNum(c.endAngle)) bad(`non-finite value in an 'arc' command`);
|
|
49
|
+
break;
|
|
50
|
+
case "ellipseArc":
|
|
51
|
+
if (!isFinitePoint(c.center) || !isFiniteNum(c.radiusX) || !isFiniteNum(c.radiusY) || !isFiniteNum(c.startAngle) || !isFiniteNum(c.endAngle)) bad(`non-finite value in an 'ellipseArc' command`);
|
|
52
|
+
break;
|
|
53
|
+
case "close": break;
|
|
54
|
+
default: bad(`command with unknown kind '${String(c.kind)}'`);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
14
57
|
/**
|
|
15
58
|
* 语义 stroke 档位 → 数值(user units)
|
|
16
59
|
* @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。
|
|
@@ -25,6 +68,93 @@ var THICKNESS_TO_WIDTH = {
|
|
|
25
68
|
veryThick: 3,
|
|
26
69
|
ultraThick: 4
|
|
27
70
|
};
|
|
71
|
+
/** 一组点的 axis-aligned 包围盒中心 */
|
|
72
|
+
var bboxCenter = (pts) => {
|
|
73
|
+
let minX = Infinity;
|
|
74
|
+
let minY = Infinity;
|
|
75
|
+
let maxX = -Infinity;
|
|
76
|
+
let maxY = -Infinity;
|
|
77
|
+
for (const [x, y] of pts) {
|
|
78
|
+
if (x < minX) minX = x;
|
|
79
|
+
if (y < minY) minY = y;
|
|
80
|
+
if (x > maxX) maxX = x;
|
|
81
|
+
if (y > maxY) maxY = y;
|
|
82
|
+
}
|
|
83
|
+
return [(minX + maxX) / 2, (minY + maxY) / 2];
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* path 整体 rotate / scale → 绕包围盒中心的 GroupPrim transforms
|
|
87
|
+
* @description rotate 写成 `{ kind:'rotate', degrees, cx, cy }`(cx/cy = bbox center),等价包一个绕同中心旋转的 Scope;
|
|
88
|
+
* scale number → `{ kind:'scale', x }`(等比,y 省略),`{x,y}` → `{ kind:'scale', x, y }`。
|
|
89
|
+
* 缩放支点同为 bbox center:用 translate(center) ∘ scale ∘ translate(-center) 三段表达。两者都缺时返回空数组。
|
|
90
|
+
* 数组顺序与 GroupPrim 渲染一致(array[0] 最外层、最后 apply):先 rotate 段再 scale 段(rotate 在外)。
|
|
91
|
+
*/
|
|
92
|
+
var buildPathTransforms = (rotate, scale, center, round) => {
|
|
93
|
+
const out = [];
|
|
94
|
+
if (rotate !== void 0) out.push({
|
|
95
|
+
kind: "rotate",
|
|
96
|
+
degrees: rotate,
|
|
97
|
+
cx: round(center[0]),
|
|
98
|
+
cy: round(center[1])
|
|
99
|
+
});
|
|
100
|
+
if (scale !== void 0) {
|
|
101
|
+
const sx = typeof scale === "number" ? scale : scale.x;
|
|
102
|
+
const sy = typeof scale === "number" ? void 0 : scale.y;
|
|
103
|
+
const scaleT = {
|
|
104
|
+
kind: "scale",
|
|
105
|
+
x: sx
|
|
106
|
+
};
|
|
107
|
+
if (sy !== void 0) scaleT.y = sy;
|
|
108
|
+
out.push({
|
|
109
|
+
kind: "translate",
|
|
110
|
+
x: round(center[0]),
|
|
111
|
+
y: round(center[1])
|
|
112
|
+
}, scaleT, {
|
|
113
|
+
kind: "translate",
|
|
114
|
+
x: round(-center[0]),
|
|
115
|
+
y: round(-center[1])
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return out;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* 把已物化的 arrow marker(局部 baseSize 坐标系,尖端 +x)按路径切线定向放到采样点
|
|
122
|
+
* @description marker 局部系:viewBox `0 0 baseSize baseSize`,参考点 (refX, baseSize/2),尖端朝 +x。
|
|
123
|
+
* GroupPrim transforms 数组语义 array[0] 最外层(最后 apply),故链 = translate(point) ∘ rotate(tangentDeg)
|
|
124
|
+
* ∘ scale(markerWidth/baseSize, markerHeight/baseSize) ∘ translate(-refX, -baseSize/2):先把参考点移到原点、
|
|
125
|
+
* 缩放到目标尺寸、绕切线角旋转、平移到采样点。marker 几何(`MarkerPrimitive[]`)是 ScenePrimitive 的结构子集,直接作 children。
|
|
126
|
+
*/
|
|
127
|
+
var buildMarkMarkerGroup = (spec, sample, round) => {
|
|
128
|
+
const angleDeg = Math.atan2(sample.tangent[1], sample.tangent[0]) * 180 / Math.PI;
|
|
129
|
+
const sx = spec.markerWidth / spec.baseSize;
|
|
130
|
+
const sy = spec.markerHeight / spec.baseSize;
|
|
131
|
+
const refY = spec.baseSize / 2;
|
|
132
|
+
return {
|
|
133
|
+
type: "group",
|
|
134
|
+
transforms: [
|
|
135
|
+
{
|
|
136
|
+
kind: "translate",
|
|
137
|
+
x: round(sample.point[0]),
|
|
138
|
+
y: round(sample.point[1])
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
kind: "rotate",
|
|
142
|
+
degrees: round(angleDeg)
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
kind: "scale",
|
|
146
|
+
x: round(sx),
|
|
147
|
+
y: round(sy)
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
kind: "translate",
|
|
151
|
+
x: round(-spec.refX),
|
|
152
|
+
y: round(-refY)
|
|
153
|
+
}
|
|
154
|
+
],
|
|
155
|
+
children: [...spec.marker]
|
|
156
|
+
};
|
|
157
|
+
};
|
|
28
158
|
/**
|
|
29
159
|
* IR Path → PathPrim
|
|
30
160
|
* @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
|
|
@@ -47,15 +177,22 @@ var emitPathPrimitive = (path, nameStack, round, measureText = require_text_metr
|
|
|
47
177
|
}
|
|
48
178
|
/** 每段 step.label 翻译出的 TextPrim(或 sloped 旋转的 group),与 path 主体同级返回 */
|
|
49
179
|
const labelPrims = [];
|
|
50
|
-
/**
|
|
180
|
+
/**
|
|
181
|
+
* 每个绘制段的几何采样器(按声明序);中段 marking 用——把整条 path 的 pos∈[0,1] 分摊到 N 段
|
|
182
|
+
* @description 与 step label 同款便宜模型:N 段等分 pos 区间,pos 落在第 ⌊pos·N⌋ 段、段内参数 = 余数;
|
|
183
|
+
* 段内 t 的几何含义随段类型(line/step 弧长、curve/cubic/bend Bezier 参数、arc 角度),由各 `*SegmentSample` 决定。
|
|
184
|
+
*/
|
|
185
|
+
const segmentSamplers = [];
|
|
186
|
+
/** 算 sample 后 emitLabelPrimitive,结果累积到 labelPrims/points;同时登记本段采样器供 marks 用 */
|
|
51
187
|
const collectLabel = (step, sampleAt) => {
|
|
188
|
+
segmentSamplers.push(sampleAt);
|
|
52
189
|
if (step.kind === "move" || step.kind === "cycle" || !("label" in step) || !step.label) return;
|
|
53
190
|
const sample = sampleAt(require_label.tForLabelPosition(step.label.position));
|
|
54
191
|
const r = require_label.emitLabelPrimitive(step.label, sample, measureText, round, path.opacity);
|
|
55
192
|
labelPrims.push(r.primitive);
|
|
56
193
|
for (const p of r.points) points.push(p);
|
|
57
194
|
};
|
|
58
|
-
const hasTo = (s) => s.kind !== "cycle" && s.kind !== "arc" && s.kind !== "circlePath" && s.kind !== "ellipsePath" && s.kind !== "rectangle";
|
|
195
|
+
const hasTo = (s) => s.kind !== "cycle" && s.kind !== "arc" && s.kind !== "circlePath" && s.kind !== "ellipsePath" && s.kind !== "rectangle" && s.kind !== "generator";
|
|
59
196
|
const anchors = steps.map((s, idx) => {
|
|
60
197
|
if (!hasTo(s)) return null;
|
|
61
198
|
const ref = require_anchor.refPointOfTarget(s.to, nameStack, scopeChain);
|
|
@@ -91,6 +228,12 @@ var emitPathPrimitive = (path, nameStack, round, measureText = require_text_metr
|
|
|
91
228
|
* @description 设置 penOverride 让下个绘制段直接用此点当 fromClip 后清空。arc=弧终点;circlePath/ellipsePath=center("画完留在圆心")
|
|
92
229
|
*/
|
|
93
230
|
let penOverride = null;
|
|
231
|
+
/**
|
|
232
|
+
* 读当前游标(生成器分支用)
|
|
233
|
+
* @description 经函数边界读 `lastEnd`,让 TS 用其声明类型(`IRPosition | null`)而非按分支位置 narrow
|
|
234
|
+
* 成字面 `null`——generator 分支在源码上位于 lastEnd 各赋值点之前,直接读会被误判恒 null
|
|
235
|
+
*/
|
|
236
|
+
const readCursor = () => lastEnd;
|
|
94
237
|
const roundPoint = (p) => [round(p[0]), round(p[1])];
|
|
95
238
|
const emitMove = (p) => {
|
|
96
239
|
const rp = roundPoint(p);
|
|
@@ -187,6 +330,72 @@ var emitPathPrimitive = (path, nameStack, round, measureText = require_text_metr
|
|
|
187
330
|
}
|
|
188
331
|
const step = steps[i];
|
|
189
332
|
if (step.kind === "move") continue;
|
|
333
|
+
if (step.kind === "generator") {
|
|
334
|
+
const generators = warnHook.effectivePathGenerators ?? {};
|
|
335
|
+
const def = Object.prototype.hasOwnProperty.call(generators, step.name) ? generators[step.name] : void 0;
|
|
336
|
+
if (!def) {
|
|
337
|
+
const available = Object.keys(generators).sort().join(", ") || "(none registered)";
|
|
338
|
+
throw new Error(`Unknown path generator '${step.name}'; available: ${available}`);
|
|
339
|
+
}
|
|
340
|
+
const parsed = def.paramsSchema.parse(step.params);
|
|
341
|
+
require_json.JsonObjectSchema.parse(parsed);
|
|
342
|
+
const paramsObj = parsed;
|
|
343
|
+
const resolvedTargets = {};
|
|
344
|
+
for (const key of def.targetParams ?? []) {
|
|
345
|
+
if (key.includes(".")) continue;
|
|
346
|
+
const raw = paramsObj[key];
|
|
347
|
+
if (raw === void 0) continue;
|
|
348
|
+
if (raw === null || typeof raw !== "string" && typeof raw !== "object") throw new Error(`path generator '${step.name}' targetParams key '${key}' must be a target (node id, coordinate, or target object); got ${raw === null ? "null" : typeof raw}.`);
|
|
349
|
+
const resolved = require_anchor.refPointOfTarget(raw, nameStack, scopeChain);
|
|
350
|
+
if (resolved) resolvedTargets[key] = resolved;
|
|
351
|
+
}
|
|
352
|
+
const prevGen = findPrev();
|
|
353
|
+
const fromGen = readCursor() ?? (prevGen ? prevGen.anchor : [0, 0]);
|
|
354
|
+
const toGen = (step.to !== void 0 ? require_anchor.refPointOfTarget(step.to, nameStack, scopeChain) : null) ?? void 0;
|
|
355
|
+
let produced;
|
|
356
|
+
try {
|
|
357
|
+
produced = def.generate({
|
|
358
|
+
from: fromGen,
|
|
359
|
+
...toGen !== void 0 ? { to: toGen } : {},
|
|
360
|
+
params: paramsObj,
|
|
361
|
+
resolvedTargets,
|
|
362
|
+
round
|
|
363
|
+
});
|
|
364
|
+
} catch (e) {
|
|
365
|
+
throw new Error(`path generator '${step.name}' threw: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
|
|
366
|
+
}
|
|
367
|
+
if (!Array.isArray(produced)) throw new Error(`path generator '${step.name}' must return an array of path commands; got ${produced === null ? "null" : typeof produced}.`);
|
|
368
|
+
for (const cmd of produced) assertValidGeneratedCommand(step.name, cmd);
|
|
369
|
+
const generated = produced;
|
|
370
|
+
startSegment(fromGen);
|
|
371
|
+
for (const cmd of generated) switch (cmd.kind) {
|
|
372
|
+
case "move":
|
|
373
|
+
startSegment(cmd.to);
|
|
374
|
+
break;
|
|
375
|
+
case "line":
|
|
376
|
+
emitLine(cmd.to);
|
|
377
|
+
break;
|
|
378
|
+
case "quad":
|
|
379
|
+
emitQuad(cmd.control, cmd.to);
|
|
380
|
+
break;
|
|
381
|
+
case "cubic":
|
|
382
|
+
emitCubic(cmd.control1, cmd.control2, cmd.to);
|
|
383
|
+
break;
|
|
384
|
+
case "arc":
|
|
385
|
+
emitArc(cmd.center, cmd.radius, cmd.startAngle, cmd.endAngle);
|
|
386
|
+
break;
|
|
387
|
+
case "ellipseArc":
|
|
388
|
+
emitEllipseArc(cmd.center, cmd.radiusX, cmd.radiusY, cmd.startAngle, cmd.endAngle);
|
|
389
|
+
break;
|
|
390
|
+
case "close":
|
|
391
|
+
emitClose();
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
const genEnd = readCursor() ?? fromGen;
|
|
395
|
+
collectLabel(step, (t) => require_segment.lineSegmentSample(fromGen, genEnd, t));
|
|
396
|
+
penOverride = readCursor();
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
190
399
|
if (step.kind === "cycle") {
|
|
191
400
|
const moveTo = lastMoveTo;
|
|
192
401
|
const prev = findPrev();
|
|
@@ -351,8 +560,8 @@ var emitPathPrimitive = (path, nameStack, round, measureText = require_text_metr
|
|
|
351
560
|
continue;
|
|
352
561
|
}
|
|
353
562
|
if (step.kind === "bend") {
|
|
354
|
-
const
|
|
355
|
-
|
|
563
|
+
const [c1, c2] = step.outAngle !== void 0 || step.inAngle !== void 0 ? require_bend.outInControlPoints(prev.anchor, currAnchor, step.outAngle ?? 0, step.inAngle ?? 180, step.looseness) : require_bend.bendControlPoints(prev.anchor, currAnchor, step.bendDirection ?? "left", step.bendAngle ?? 30);
|
|
564
|
+
if (!isFinitePoint(c1) || !isFinitePoint(c2)) throw new Error("Bend produced a non-finite control point (looseness / angle too large); use smaller values.");
|
|
356
565
|
const fromClip = usedOverride ?? require_anchor.clipForTarget(prev.step.to, c1, nameStack, scopeChain);
|
|
357
566
|
const toClip = require_anchor.clipForTarget(step.to, c2, nameStack, scopeChain);
|
|
358
567
|
if (!fromClip || !toClip) return null;
|
|
@@ -383,11 +592,50 @@ var emitPathPrimitive = (path, nameStack, round, measureText = require_text_metr
|
|
|
383
592
|
fillOpacity: path.fillOpacity,
|
|
384
593
|
strokeOpacity: path.drawOpacity
|
|
385
594
|
};
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
const
|
|
595
|
+
const effectiveArrows = warnHook.effectiveArrows ?? require_index.BUILTIN_ARROWS;
|
|
596
|
+
const arrows = require_shrink.endpointArrows(path.arrow, path.arrowDetail, effectiveArrows, round);
|
|
597
|
+
const markPrims = [];
|
|
598
|
+
if (path.marks && path.marks.length > 0 && segmentSamplers.length > 0) {
|
|
599
|
+
const segCount = segmentSamplers.length;
|
|
600
|
+
for (const { pos, mark } of path.marks) {
|
|
601
|
+
const scaled = pos * segCount;
|
|
602
|
+
const segIdx = Math.min(Math.floor(scaled), segCount - 1);
|
|
603
|
+
const localT = scaled - segIdx;
|
|
604
|
+
const sample = segmentSamplers[segIdx](pos === 1 ? 1 : localT);
|
|
605
|
+
const spec = require_shrink.resolveMarkArrowSpec(mark, effectiveArrows, round);
|
|
606
|
+
markPrims.push(buildMarkMarkerGroup(spec, sample, round));
|
|
607
|
+
points.push(sample.point);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
require_shrink.applyArrowShrinks(commands, arrows.shrinkStart, arrows.shrinkEnd, strokeWidth, round);
|
|
611
|
+
const endpointSpecs = {};
|
|
612
|
+
if (arrows.arrowStart) endpointSpecs.arrowStart = arrows.arrowStart;
|
|
613
|
+
if (arrows.arrowEnd) endpointSpecs.arrowEnd = arrows.arrowEnd;
|
|
614
|
+
const { primitive } = require_split.splitSubPathsForEndpointArrows(commands, baseProps, endpointSpecs);
|
|
615
|
+
const bodyPrims = [
|
|
616
|
+
primitive,
|
|
617
|
+
...labelPrims,
|
|
618
|
+
...markPrims
|
|
619
|
+
];
|
|
620
|
+
if ((path.rotate !== void 0 || path.scale !== void 0) && points.length > 0) {
|
|
621
|
+
const center = bboxCenter(points);
|
|
622
|
+
const transforms = buildPathTransforms(path.rotate, path.scale, center, round);
|
|
623
|
+
if (transforms.length > 0) {
|
|
624
|
+
const group = {
|
|
625
|
+
type: "group",
|
|
626
|
+
transforms,
|
|
627
|
+
children: bodyPrims
|
|
628
|
+
};
|
|
629
|
+
const transformedPoints = points.map((p) => require_scope.applyTransformChain(p, transforms));
|
|
630
|
+
if (!transformedPoints.every(isFinitePoint)) throw new Error("Path rotate / scale produced a non-finite coordinate (scale too large); use a smaller scale.");
|
|
631
|
+
return {
|
|
632
|
+
primitives: [group],
|
|
633
|
+
points: transformedPoints
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
}
|
|
389
637
|
return {
|
|
390
|
-
primitives:
|
|
638
|
+
primitives: bodyPrims,
|
|
391
639
|
points
|
|
392
640
|
};
|
|
393
641
|
};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { PaintResolver } from '../paint';
|
|
2
2
|
import { IRPath, IRPosition } from '../../ir';
|
|
3
3
|
import { ScenePrimitive, Transform } from '../../primitive';
|
|
4
|
+
import { PathGeneratorDefinition } from '../../pathGenerators';
|
|
4
5
|
import { NameStack } from '../name-stack';
|
|
5
6
|
import { TextMeasurer } from '../text-metrics';
|
|
7
|
+
import { EffectiveArrows } from './shrink';
|
|
6
8
|
/** emitPathPrimitive 可选 warn 钩子 */
|
|
7
9
|
export type EmitPathWarnHook = {
|
|
8
10
|
/** 警告收集器(由 compileToScene 传入) */
|
|
@@ -21,6 +23,19 @@ export type EmitPathWarnHook = {
|
|
|
21
23
|
scopeChain?: ReadonlyArray<Transform>;
|
|
22
24
|
/** fill 解析器(PaintSpec → resourceRef + 登记资源);缺省时纯色透传、PaintSpec 退化为无填充 */
|
|
23
25
|
resolveFill?: PaintResolver;
|
|
26
|
+
/**
|
|
27
|
+
* 有效 arrow 表(内置 7 + 注入);缺省 = 仅内置 7
|
|
28
|
+
* @description compileToScene 合并 `{ ...BUILTIN_ARROWS, ...options.arrows }` 传入;
|
|
29
|
+
* endpointArrows 据此查表算 shrink / 调 def.emit;未注册名编译期 throw
|
|
30
|
+
*/
|
|
31
|
+
effectiveArrows?: EffectiveArrows;
|
|
32
|
+
/**
|
|
33
|
+
* 有效 path generator 表(注入即全部,core 无内置);缺省 = 空表
|
|
34
|
+
* @description compileToScene 传 `options.pathGenerators ?? {}`;generator step 据此查表(未注册名
|
|
35
|
+
* 编译期 throw,错误列出可用名)→ 双 parse 护栏 → targetParams resolve → 调 generate splice 命令。
|
|
36
|
+
* 解析逻辑由后续实现落地(此处仅声明 hook 入口)。
|
|
37
|
+
*/
|
|
38
|
+
effectivePathGenerators?: Record<string, PathGeneratorDefinition>;
|
|
24
39
|
};
|
|
25
40
|
/**
|
|
26
41
|
* IR Path → PathPrim
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/compile/path/index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAY9C,OAAO,KAAK,EACV,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/compile/path/index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAY9C,OAAO,KAAK,EACV,MAAM,EAEN,UAAU,EAGX,MAAM,UAAU,CAAC;AAElB,OAAO,KAAK,EAIV,cAAc,EACd,SAAS,EACV,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,KAAK,YAAY,EAAoB,MAAM,iBAAiB,CAAC;AAKtE,OAAO,EAAE,KAAK,eAAe,EAA2D,MAAM,UAAU,CAAC;AAsFzG,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;IACtC,wEAAwE;IACxE,WAAW,CAAC,EAAE,aAAa,CAAC;IAC5B;;;;OAIG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC;;;;;OAKG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;CACnE,CAAC;AA0EF;;;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,IA0tBrE,CAAC"}
|
|
@@ -32,6 +32,14 @@ var normalizeRelativeTargets = (steps, nameStack, scopeChain = []) => {
|
|
|
32
32
|
out.push(step);
|
|
33
33
|
continue;
|
|
34
34
|
}
|
|
35
|
+
if (step.kind === "generator") {
|
|
36
|
+
out.push(step);
|
|
37
|
+
if (step.to !== void 0) {
|
|
38
|
+
const pos = require_anchor.refPointOfTarget(step.to, nameStack, scopeChain);
|
|
39
|
+
if (pos) prevEnd = pos;
|
|
40
|
+
}
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
35
43
|
const original = step.to;
|
|
36
44
|
let resolvedTo = original;
|
|
37
45
|
let updatePrevEnd = true;
|
|
@@ -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,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,
|
|
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,CAuFd,CAAC"}
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
const require_arrow = require("../../ir/path/arrow.cjs");
|
|
2
|
+
const require_marker_prim = require("../marker-prim.cjs");
|
|
2
3
|
const require_anchor = require("./anchor.cjs");
|
|
3
|
-
const require_arrow_geometry = require("./arrow-geometry.cjs");
|
|
4
4
|
//#region src/compile/path/shrink.ts
|
|
5
|
+
/** 默认 baseSize(marker 局部基准边长,viewBox `0 0 baseSize baseSize`) */
|
|
6
|
+
var ARROW_GEOMETRY_BASE_SIZE = 10;
|
|
7
|
+
/** 查 effective 表取 def;未注册名编译期 throw(消息含字母序可用名列表) */
|
|
8
|
+
var lookupArrowDef = (shape, effective) => {
|
|
9
|
+
if (Object.prototype.hasOwnProperty.call(effective, shape)) return effective[shape];
|
|
10
|
+
const available = Object.keys(effective).sort().join(", ");
|
|
11
|
+
throw new Error(`Unknown arrow shape '${shape}'; available: ${available}`);
|
|
12
|
+
};
|
|
5
13
|
/**
|
|
6
|
-
*
|
|
7
|
-
* @description 缺省字段继承顶层(不是"完全替换");空心
|
|
14
|
+
* 端点级视觉输入:顶层默认 ⊕ end-side override(逐字段 merge)
|
|
15
|
+
* @description 缺省字段继承顶层(不是"完全替换");空心 def 上 fill 字段被丢(silent no-op)。
|
|
16
|
+
* 产 compile-internal 中间体,供 shrink + emit 消费。
|
|
8
17
|
*/
|
|
9
|
-
var
|
|
18
|
+
var resolveArrowVisual = (topLevel, endSide, effective) => {
|
|
10
19
|
const baseShape = endSide?.shape ?? topLevel.shape ?? require_arrow.DEFAULT_ARROW_SHAPE;
|
|
11
20
|
const out = { shape: baseShape };
|
|
12
21
|
const scale = endSide?.scale ?? topLevel.scale;
|
|
@@ -21,35 +30,148 @@ var resolveArrowEndSpec = (topLevel, endSide) => {
|
|
|
21
30
|
if (opacity !== void 0) out.opacity = opacity;
|
|
22
31
|
const lineWidth = endSide?.lineWidth ?? topLevel.lineWidth;
|
|
23
32
|
if (lineWidth !== void 0) out.lineWidth = lineWidth;
|
|
24
|
-
if (!
|
|
33
|
+
if (!lookupArrowDef(baseShape, effective).hollow) {
|
|
25
34
|
const fill = endSide?.fill ?? topLevel.fill;
|
|
26
35
|
if (fill !== void 0) out.fill = fill;
|
|
27
36
|
}
|
|
28
37
|
return out;
|
|
29
38
|
};
|
|
30
|
-
/**
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
/**
|
|
40
|
+
* 校验 def 几何字段有限(baseSize 还须 > 0)
|
|
41
|
+
* @description 第三方 / LLM 写出的 def 可能漏字段或塞 NaN / Infinity / 0 baseSize;这些数会经 shrink 公式
|
|
42
|
+
* 污染 path 本体坐标(非仅 marker),故在此抛清晰错(含 shape 名,便于自修),不放任 NaN 静默流出。
|
|
43
|
+
*/
|
|
44
|
+
var assertFiniteGeometry = (shape, def) => {
|
|
45
|
+
if (!Number.isFinite(def.lineContactX)) throw new Error(`Arrow '${shape}' has a non-finite lineContactX (${String(def.lineContactX)}); it must be a finite number.`);
|
|
46
|
+
if (def.baseSize !== void 0 && (!Number.isFinite(def.baseSize) || def.baseSize <= 0)) throw new Error(`Arrow '${shape}' has an invalid baseSize (${String(def.baseSize)}); it must be a finite number greater than 0.`);
|
|
47
|
+
if (def.tipX !== void 0 && !Number.isFinite(def.tipX)) throw new Error(`Arrow '${shape}' has a non-finite tipX (${String(def.tipX)}); it must be a finite number.`);
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* 调 def.emit 收集 marker 并跑窄子集 + JSON-safe 校验
|
|
51
|
+
* @description emit 缺失 / 非函数 / 抛错 / 返回非 iterable 都包成含 shape 名的清晰错(便于第三方 / LLM 自修),
|
|
52
|
+
* 不泄漏内部变量名;产物逐个过共享 `validateMarkerPrimitives`(窄子集 + 深度无函数检查,与 pattern motif 同套)。
|
|
53
|
+
*/
|
|
54
|
+
var callEmit = (shape, def, ctx) => {
|
|
55
|
+
if (typeof def.emit !== "function") throw new Error(`Arrow '${shape}' is missing an emit function (ArrowDefinition.emit is required).`);
|
|
56
|
+
let marker;
|
|
57
|
+
try {
|
|
58
|
+
marker = [...def.emit(ctx)];
|
|
59
|
+
} catch (e) {
|
|
60
|
+
throw new Error(`Arrow '${shape}' emit failed: ${e instanceof Error ? e.message : String(e)}`, { cause: e });
|
|
43
61
|
}
|
|
62
|
+
require_marker_prim.validateMarkerPrimitives(`Arrow '${shape}'`, marker);
|
|
63
|
+
return marker;
|
|
64
|
+
};
|
|
65
|
+
/** 据 def + 视觉输入解析端点几何(baseSize / tipX / contactX / resolved length·width) */
|
|
66
|
+
var resolveGeometry = (visual, effective) => {
|
|
67
|
+
const def = lookupArrowDef(visual.shape, effective);
|
|
68
|
+
assertFiniteGeometry(visual.shape, def);
|
|
69
|
+
const baseSize = def.baseSize ?? ARROW_GEOMETRY_BASE_SIZE;
|
|
70
|
+
const tipX = def.tipX ?? baseSize;
|
|
71
|
+
const lineWidth = visual.lineWidth ?? 1.5;
|
|
72
|
+
const contactX = def.hollow ? def.lineContactX - lineWidth / 2 : def.lineContactX;
|
|
73
|
+
const scale = visual.scale ?? 1;
|
|
74
|
+
const resolvedLength = (visual.length ?? def.defaultLength ?? 6) * scale;
|
|
75
|
+
const resolvedWidth = (visual.width ?? def.defaultWidth ?? 6) * scale;
|
|
76
|
+
if (!Number.isFinite(resolvedLength) || !Number.isFinite(resolvedWidth)) throw new Error(`Arrow '${visual.shape}' resolved length/width is non-finite (length × scale overflowed); use smaller length / scale values.`);
|
|
77
|
+
return {
|
|
78
|
+
def,
|
|
79
|
+
baseSize,
|
|
80
|
+
tipX,
|
|
81
|
+
contactX,
|
|
82
|
+
lineWidth,
|
|
83
|
+
resolvedLength,
|
|
84
|
+
resolvedWidth
|
|
85
|
+
};
|
|
44
86
|
};
|
|
45
87
|
/**
|
|
46
88
|
* 端点级 shrink(strokeWidth 倍):line 末端朝起点缩这么多,让箭头尖端落回原 target
|
|
47
89
|
* @description 不分实心 / 空心:路径端点接在箭头尾部或凹口,箭头尖端仍贴原 target。低 opacity 下不会再透出 line。
|
|
48
90
|
*/
|
|
49
|
-
var computeShrink = (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
91
|
+
var computeShrink = (geometry) => (geometry.tipX - geometry.contactX) * geometry.resolvedLength / geometry.baseSize;
|
|
92
|
+
/**
|
|
93
|
+
* 据 def + 解析后视觉输入构 `ArrowEmitContext`
|
|
94
|
+
* @description hollow def:fill 丢(neutral)、stroke = color override ?? contextStroke、lineWidth 启用;
|
|
95
|
+
* solid def:fill = fill ?? color ?? contextStroke、stroke = color ?? contextStroke。
|
|
96
|
+
*/
|
|
97
|
+
var buildEmitContext = (visual, geometry, round) => {
|
|
98
|
+
const contextStroke = { kind: "contextStroke" };
|
|
99
|
+
return {
|
|
100
|
+
stroke: visual.color ?? contextStroke,
|
|
101
|
+
fill: geometry.def.hollow ? contextStroke : visual.fill ?? visual.color ?? contextStroke,
|
|
102
|
+
lineWidth: geometry.lineWidth,
|
|
103
|
+
round
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* 把视觉中间体物化成最终 `ArrowEndSpec`(已解析 marker 描述)
|
|
108
|
+
* @description 构 `ArrowEmitContext` → 调 `def.emit` 收集 `MarkerPrimitive[]`,并算 baseSize /
|
|
109
|
+
* refX(hollow 减 lineWidth/2)/ markerWidth = 解析 length / markerHeight = 解析 width / opacity 透传。
|
|
110
|
+
*/
|
|
111
|
+
var materializeArrowEndSpec = (visual, geometry, round) => {
|
|
112
|
+
const ctx = buildEmitContext(visual, geometry, round);
|
|
113
|
+
const marker = callEmit(visual.shape, geometry.def, ctx);
|
|
114
|
+
const out = {
|
|
115
|
+
shape: visual.shape,
|
|
116
|
+
baseSize: geometry.baseSize,
|
|
117
|
+
refX: geometry.contactX,
|
|
118
|
+
markerWidth: geometry.resolvedLength,
|
|
119
|
+
markerHeight: geometry.resolvedWidth,
|
|
120
|
+
marker
|
|
121
|
+
};
|
|
122
|
+
if (visual.opacity !== void 0) out.opacity = visual.opacity;
|
|
123
|
+
return out;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* IR path-level `arrow` + `arrowDetail` → PathPrim 起末端点已解析 marker 描述
|
|
127
|
+
* @description merge 视觉输入 → 查 effective 表 + 解析几何 → 算 shrink → 调 def.emit 物化最终 `ArrowEndSpec`。
|
|
128
|
+
* 返回同时带 compile-internal 的 shrink 量(端点收缩在 compile 落,与 emit 落点无关)。
|
|
129
|
+
* 未注册 shape 名在此 throw(lookupArrowDef)。
|
|
130
|
+
*/
|
|
131
|
+
var endpointArrows = (arrow, detail, effective, round) => {
|
|
132
|
+
if (!arrow || arrow === "none") return {
|
|
133
|
+
shrinkStart: 0,
|
|
134
|
+
shrinkEnd: 0
|
|
135
|
+
};
|
|
136
|
+
const top = detail ?? {};
|
|
137
|
+
const wantStart = arrow === "<-" || arrow === "<->";
|
|
138
|
+
const wantEnd = arrow === "->" || arrow === "<->";
|
|
139
|
+
const result = {
|
|
140
|
+
shrinkStart: 0,
|
|
141
|
+
shrinkEnd: 0
|
|
142
|
+
};
|
|
143
|
+
if (wantStart) {
|
|
144
|
+
const visual = resolveArrowVisual(top, top.start, effective);
|
|
145
|
+
const geometry = resolveGeometry(visual, effective);
|
|
146
|
+
result.arrowStart = materializeArrowEndSpec(visual, geometry, round);
|
|
147
|
+
result.shrinkStart = computeShrink(geometry);
|
|
148
|
+
}
|
|
149
|
+
if (wantEnd) {
|
|
150
|
+
const visual = resolveArrowVisual(top, top.end, effective);
|
|
151
|
+
const geometry = resolveGeometry(visual, effective);
|
|
152
|
+
result.arrowEnd = materializeArrowEndSpec(visual, geometry, round);
|
|
153
|
+
result.shrinkEnd = computeShrink(geometry);
|
|
154
|
+
}
|
|
155
|
+
return result;
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* 解析一个中段标记 `IRArrowMark` 为已物化的 marker 描述(`ArrowEndSpec`)
|
|
159
|
+
* @description 复用端点箭头同一管线:mark 自身视觉子集字段(shape / scale / length / width / color /
|
|
160
|
+
* fill / opacity / lineWidth)即 `ResolvedArrowVisual`(空心 def 上 fill 字段被丢)→ 查 effective 表
|
|
161
|
+
* 解析几何 → 调 def.emit 物化局部 baseSize 几何 + wrapper 参数。方向由调用方按路径切线决定,本函数不含定向。
|
|
162
|
+
* 未注册 shape 名在此 throw(lookupArrowDef)。
|
|
163
|
+
*/
|
|
164
|
+
var resolveMarkArrowSpec = (mark, effective, round) => {
|
|
165
|
+
const baseShape = mark.shape ?? require_arrow.DEFAULT_ARROW_SHAPE;
|
|
166
|
+
const visual = { shape: baseShape };
|
|
167
|
+
if (mark.scale !== void 0) visual.scale = mark.scale;
|
|
168
|
+
if (mark.length !== void 0) visual.length = mark.length;
|
|
169
|
+
if (mark.width !== void 0) visual.width = mark.width;
|
|
170
|
+
if (mark.color !== void 0) visual.color = mark.color;
|
|
171
|
+
if (mark.opacity !== void 0) visual.opacity = mark.opacity;
|
|
172
|
+
if (mark.lineWidth !== void 0) visual.lineWidth = mark.lineWidth;
|
|
173
|
+
if (!lookupArrowDef(baseShape, effective).hollow && mark.fill !== void 0) visual.fill = mark.fill;
|
|
174
|
+
return materializeArrowEndSpec(visual, resolveGeometry(visual, effective), round);
|
|
53
175
|
};
|
|
54
176
|
/** 取一个 PathCommand 末端 endpoint(move/line/quad/cubic → to;arc/ellipseArc → polar(end);close 无端点) */
|
|
55
177
|
var endpointOf = (cmd) => {
|
|
@@ -92,7 +214,7 @@ var setEndpoint = (commands, idx, newPt, round) => {
|
|
|
92
214
|
* @description 让 line 端点接在 hollow arrow 尾部外缘、不贯穿 back outline;shrink=0 的实心 shape 跳过。in-place 改写 commands 数组
|
|
93
215
|
*/
|
|
94
216
|
var applyArrowShrinks = (commands, shrinkStart, shrinkEnd, strokeWidth, round) => {
|
|
95
|
-
if (shrinkStart
|
|
217
|
+
if (shrinkStart !== 0) {
|
|
96
218
|
const firstIdx = commands.findIndex((o) => o.kind === "move");
|
|
97
219
|
if (firstIdx >= 0) {
|
|
98
220
|
const cur = commands[firstIdx];
|
|
@@ -103,7 +225,7 @@ var applyArrowShrinks = (commands, shrinkStart, shrinkEnd, strokeWidth, round) =
|
|
|
103
225
|
}
|
|
104
226
|
}
|
|
105
227
|
}
|
|
106
|
-
if (shrinkEnd
|
|
228
|
+
if (shrinkEnd !== 0) {
|
|
107
229
|
let lastIdx = -1;
|
|
108
230
|
for (let i = commands.length - 1; i >= 0; i--) if (commands[i].kind !== "close") {
|
|
109
231
|
lastIdx = i;
|
|
@@ -125,5 +247,5 @@ var applyArrowShrinks = (commands, shrinkStart, shrinkEnd, strokeWidth, round) =
|
|
|
125
247
|
};
|
|
126
248
|
//#endregion
|
|
127
249
|
exports.applyArrowShrinks = applyArrowShrinks;
|
|
128
|
-
exports.computeShrink = computeShrink;
|
|
129
250
|
exports.endpointArrows = endpointArrows;
|
|
251
|
+
exports.resolveMarkArrowSpec = resolveMarkArrowSpec;
|
|
@@ -1,15 +1,28 @@
|
|
|
1
|
-
import { IRArrowDetail } from '../../ir';
|
|
1
|
+
import { IRArrowDetail, IRArrowMark } from '../../ir';
|
|
2
|
+
import { ArrowDefinition } from '../../arrows';
|
|
2
3
|
import { ArrowEndSpec, PathCommand } from '../../primitive';
|
|
3
|
-
/**
|
|
4
|
-
export
|
|
4
|
+
/** 有效 arrow 表:内置 7 + 注入(同名注入覆盖内置) */
|
|
5
|
+
export type EffectiveArrows = Record<string, ArrowDefinition>;
|
|
6
|
+
/**
|
|
7
|
+
* IR path-level `arrow` + `arrowDetail` → PathPrim 起末端点已解析 marker 描述
|
|
8
|
+
* @description merge 视觉输入 → 查 effective 表 + 解析几何 → 算 shrink → 调 def.emit 物化最终 `ArrowEndSpec`。
|
|
9
|
+
* 返回同时带 compile-internal 的 shrink 量(端点收缩在 compile 落,与 emit 落点无关)。
|
|
10
|
+
* 未注册 shape 名在此 throw(lookupArrowDef)。
|
|
11
|
+
*/
|
|
12
|
+
export declare const endpointArrows: (arrow: "none" | "->" | "<-" | "<->" | undefined, detail: IRArrowDetail | undefined, effective: EffectiveArrows, round: (n: number) => number) => {
|
|
5
13
|
arrowStart?: ArrowEndSpec;
|
|
6
14
|
arrowEnd?: ArrowEndSpec;
|
|
15
|
+
shrinkStart: number;
|
|
16
|
+
shrinkEnd: number;
|
|
7
17
|
};
|
|
8
18
|
/**
|
|
9
|
-
*
|
|
10
|
-
* @description
|
|
19
|
+
* 解析一个中段标记 `IRArrowMark` 为已物化的 marker 描述(`ArrowEndSpec`)
|
|
20
|
+
* @description 复用端点箭头同一管线:mark 自身视觉子集字段(shape / scale / length / width / color /
|
|
21
|
+
* fill / opacity / lineWidth)即 `ResolvedArrowVisual`(空心 def 上 fill 字段被丢)→ 查 effective 表
|
|
22
|
+
* 解析几何 → 调 def.emit 物化局部 baseSize 几何 + wrapper 参数。方向由调用方按路径切线决定,本函数不含定向。
|
|
23
|
+
* 未注册 shape 名在此 throw(lookupArrowDef)。
|
|
11
24
|
*/
|
|
12
|
-
export declare const
|
|
25
|
+
export declare const resolveMarkArrowSpec: (mark: IRArrowMark, effective: EffectiveArrows, round: (n: number) => number) => ArrowEndSpec;
|
|
13
26
|
/**
|
|
14
27
|
* 按 shape + spec(length / scale / lineWidth)把首/末段端点向内缩短
|
|
15
28
|
* @description 让 line 端点接在 hollow arrow 尾部外缘、不贯穿 back outline;shrink=0 的实心 shape 跳过。in-place 改写 commands 数组
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shrink.d.ts","sourceRoot":"","sources":["../../../../src/compile/path/shrink.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"shrink.d.ts","sourceRoot":"","sources":["../../../../src/compile/path/shrink.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,aAAa,EAElB,KAAK,WAAW,EAEjB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,eAAe,EAAoB,MAAM,cAAc,CAAC;AACtE,OAAO,KAAK,EAAE,YAAY,EAA+B,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAI9F,qCAAqC;AACrC,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAwM9D;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GACzB,OAAO,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,SAAS,EAC/C,QAAQ,aAAa,GAAG,SAAS,EACjC,WAAW,eAAe,EAC1B,OAAO,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,KAC3B;IACD,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CAyBnB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,GAC/B,MAAM,WAAW,EACjB,WAAW,eAAe,EAC1B,OAAO,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,KAC3B,YAaF,CAAC;AAiDF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAC5B,UAAU,KAAK,CAAC,WAAW,CAAC,EAC5B,aAAa,MAAM,EACnB,WAAW,MAAM,EACjB,aAAa,MAAM,EACnB,OAAO,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,KAC3B,IA4CF,CAAC"}
|