@retikz/core 0.1.0-alpha.0 → 0.1.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/index.d.ts +1 -0
- package/dist/es/compile/index.d.ts.map +1 -1
- package/dist/es/compile/node.d.ts +73 -22
- package/dist/es/compile/node.d.ts.map +1 -1
- package/dist/es/compile/node.js +276 -52
- package/dist/es/compile/parseTarget.d.ts +25 -0
- package/dist/es/compile/parseTarget.d.ts.map +1 -0
- package/dist/es/compile/parseTarget.js +48 -0
- package/dist/es/compile/path.d.ts +17 -7
- package/dist/es/compile/path.d.ts.map +1 -1
- package/dist/es/compile/path.js +276 -33
- package/dist/es/compile/text-metrics.d.ts +2 -2
- package/dist/es/compile/text-metrics.d.ts.map +1 -1
- package/dist/es/geometry/circle.d.ts +32 -0
- package/dist/es/geometry/circle.d.ts.map +1 -0
- package/dist/es/geometry/circle.js +79 -0
- package/dist/es/geometry/diamond.d.ts +44 -0
- package/dist/es/geometry/diamond.d.ts.map +1 -0
- package/dist/es/geometry/diamond.js +87 -0
- package/dist/es/geometry/ellipse.d.ts +38 -0
- package/dist/es/geometry/ellipse.d.ts.map +1 -0
- package/dist/es/geometry/ellipse.js +86 -0
- package/dist/es/geometry/index.d.ts +3 -0
- package/dist/es/geometry/index.d.ts.map +1 -1
- package/dist/es/index.d.ts +9 -7
- package/dist/es/index.d.ts.map +1 -1
- package/dist/es/index.js +8 -4
- package/dist/es/ir/node.d.ts +319 -7
- package/dist/es/ir/node.d.ts.map +1 -1
- package/dist/es/ir/node.js +96 -8
- package/dist/es/ir/path/arrow.d.ts +26 -0
- package/dist/es/ir/path/arrow.d.ts.map +1 -0
- package/dist/es/ir/path/arrow.js +25 -0
- package/dist/es/ir/path/index.d.ts +1 -0
- package/dist/es/ir/path/index.d.ts.map +1 -1
- package/dist/es/ir/path/path.d.ts +60 -0
- package/dist/es/ir/path/path.d.ts.map +1 -1
- package/dist/es/ir/path/path.js +10 -0
- package/dist/es/ir/path/step.d.ts +56 -2
- package/dist/es/ir/path/step.d.ts.map +1 -1
- package/dist/es/ir/path/step.js +17 -2
- package/dist/es/ir/scene.d.ts +538 -16
- package/dist/es/ir/scene.d.ts.map +1 -1
- package/dist/es/parsers/parseWay.d.ts +56 -10
- package/dist/es/parsers/parseWay.d.ts.map +1 -1
- package/dist/es/parsers/parseWay.js +68 -6
- package/dist/es/primitive/ellipse.d.ts +34 -0
- package/dist/es/primitive/ellipse.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/path.d.ts +11 -0
- package/dist/es/primitive/path.d.ts.map +1 -1
- package/dist/es/primitive/rect.d.ts +2 -0
- package/dist/es/primitive/rect.d.ts.map +1 -1
- package/dist/es/primitive/scene.d.ts +2 -1
- package/dist/es/primitive/scene.d.ts.map +1 -1
- package/dist/es/primitive/text.d.ts +43 -13
- package/dist/es/primitive/text.d.ts.map +1 -1
- package/dist/es/types.d.ts +3 -0
- package/dist/es/types.d.ts.map +1 -0
- package/dist/lib/compile/index.d.ts +1 -0
- package/dist/lib/compile/index.d.ts.map +1 -1
- package/dist/lib/compile/node.cjs +278 -52
- package/dist/lib/compile/node.d.ts +73 -22
- package/dist/lib/compile/node.d.ts.map +1 -1
- package/dist/lib/compile/parseTarget.cjs +48 -0
- package/dist/lib/compile/parseTarget.d.ts +25 -0
- package/dist/lib/compile/parseTarget.d.ts.map +1 -0
- package/dist/lib/compile/path.cjs +275 -32
- package/dist/lib/compile/path.d.ts +17 -7
- package/dist/lib/compile/path.d.ts.map +1 -1
- package/dist/lib/compile/text-metrics.d.ts +2 -2
- package/dist/lib/compile/text-metrics.d.ts.map +1 -1
- package/dist/lib/geometry/circle.cjs +79 -0
- package/dist/lib/geometry/circle.d.ts +32 -0
- package/dist/lib/geometry/circle.d.ts.map +1 -0
- package/dist/lib/geometry/diamond.cjs +87 -0
- package/dist/lib/geometry/diamond.d.ts +44 -0
- package/dist/lib/geometry/diamond.d.ts.map +1 -0
- package/dist/lib/geometry/ellipse.cjs +86 -0
- package/dist/lib/geometry/ellipse.d.ts +38 -0
- package/dist/lib/geometry/ellipse.d.ts.map +1 -0
- package/dist/lib/geometry/index.d.ts +3 -0
- package/dist/lib/geometry/index.d.ts.map +1 -1
- package/dist/lib/index.cjs +16 -0
- package/dist/lib/index.d.ts +9 -7
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/ir/node.cjs +100 -7
- package/dist/lib/ir/node.d.ts +319 -7
- package/dist/lib/ir/node.d.ts.map +1 -1
- package/dist/lib/ir/path/arrow.cjs +25 -0
- package/dist/lib/ir/path/arrow.d.ts +26 -0
- package/dist/lib/ir/path/arrow.d.ts.map +1 -0
- package/dist/lib/ir/path/index.d.ts +1 -0
- package/dist/lib/ir/path/index.d.ts.map +1 -1
- package/dist/lib/ir/path/path.cjs +10 -0
- package/dist/lib/ir/path/path.d.ts +60 -0
- package/dist/lib/ir/path/path.d.ts.map +1 -1
- package/dist/lib/ir/path/step.cjs +18 -1
- package/dist/lib/ir/path/step.d.ts +56 -2
- package/dist/lib/ir/path/step.d.ts.map +1 -1
- package/dist/lib/ir/scene.d.ts +538 -16
- package/dist/lib/ir/scene.d.ts.map +1 -1
- package/dist/lib/parsers/parseWay.cjs +68 -5
- package/dist/lib/parsers/parseWay.d.ts +56 -10
- package/dist/lib/parsers/parseWay.d.ts.map +1 -1
- package/dist/lib/primitive/ellipse.d.ts +34 -0
- package/dist/lib/primitive/ellipse.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/path.d.ts +11 -0
- package/dist/lib/primitive/path.d.ts.map +1 -1
- package/dist/lib/primitive/rect.d.ts +2 -0
- package/dist/lib/primitive/rect.d.ts.map +1 -1
- package/dist/lib/primitive/scene.d.ts +2 -1
- package/dist/lib/primitive/scene.d.ts.map +1 -1
- package/dist/lib/primitive/text.d.ts +43 -13
- package/dist/lib/primitive/text.d.ts.map +1 -1
- package/dist/lib/types.d.ts +3 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/package.json +1 -1
|
@@ -1,52 +1,295 @@
|
|
|
1
|
-
const require_rect = require("../geometry/rect.cjs");
|
|
2
1
|
const require_position = require("./position.cjs");
|
|
3
2
|
const require_node = require("./node.cjs");
|
|
3
|
+
const require_parseTarget = require("./parseTarget.cjs");
|
|
4
4
|
//#region src/compile/path.ts
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* - Position / PolarPosition → 解析后的笛卡尔
|
|
9
|
-
* 解析失败返回 null(与 resolvePosition 一致)。
|
|
6
|
+
* IR 的 path-level `arrow` + `arrowShape` 映射到 PathPrim 的
|
|
7
|
+
* arrowStart / arrowEnd(值就是 shape 名)。`arrowShape` 省略时默认 'normal'。
|
|
10
8
|
*/
|
|
11
|
-
var
|
|
9
|
+
var arrowMarkers = (arrow, shape = "normal") => {
|
|
10
|
+
switch (arrow) {
|
|
11
|
+
case "->": return { arrowEnd: shape };
|
|
12
|
+
case "<-": return { arrowStart: shape };
|
|
13
|
+
case "<->": return {
|
|
14
|
+
arrowStart: shape,
|
|
15
|
+
arrowEnd: shape
|
|
16
|
+
};
|
|
17
|
+
default: return {};
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* 按 arrow shape 决定线段需要从端点向内"缩短"多少(单位:strokeWidth 倍)。
|
|
22
|
+
*
|
|
23
|
+
* 用途:避免 hollow shape(如 `open` / `openDiamond` / `openCircle`)的
|
|
24
|
+
* 空心内部被 path 描边穿过——把线末端退到形状背面位置,marker 的 apex
|
|
25
|
+
* 才能正好落在原始端点上。
|
|
26
|
+
*
|
|
27
|
+
* 这个值与 marker 几何配套,必须与 react/render/arrowMarkers.tsx 里
|
|
28
|
+
* 各 shape 的 refX / 形状定义保持一致:
|
|
29
|
+
* shrink = (apexX - refX) × markerWidth / viewBoxWidth
|
|
30
|
+
* open: apexX=9, refX=1, scale=6/10 → 8 × 0.6 = 4.8
|
|
31
|
+
* openDiamond: apexX=9, refX=1, scale=6/10 → 8 × 0.6 = 4.8
|
|
32
|
+
* openCircle: apexX=10, refX=0, scale=6/10 → 10 × 0.6 = 6
|
|
33
|
+
* 实心 shape(normal / stealth / diamond / circle)apex / 边缘已贴 refX=10,
|
|
34
|
+
* line 被 fill 覆盖看不见,shrink=0。
|
|
35
|
+
*/
|
|
36
|
+
var SHRINK_FOR_SHAPE = {
|
|
37
|
+
normal: 0,
|
|
38
|
+
open: 4.8,
|
|
39
|
+
stealth: 0,
|
|
40
|
+
diamond: 0,
|
|
41
|
+
openDiamond: 4.8,
|
|
42
|
+
circle: 0,
|
|
43
|
+
openCircle: 6
|
|
44
|
+
};
|
|
45
|
+
/** 把点 p 朝 target 方向移动 dist 个 path 单位 */
|
|
46
|
+
var shiftToward = (p, target, dist) => {
|
|
47
|
+
const dx = target[0] - p[0];
|
|
48
|
+
const dy = target[1] - p[1];
|
|
49
|
+
const len = Math.sqrt(dx * dx + dy * dy);
|
|
50
|
+
if (len === 0 || dist === 0) return p;
|
|
51
|
+
return [p[0] + dx / len * dist, p[1] + dy / len * dist];
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* 求一个 step.to 的"参考点"——给段内 boundary clip 算方向 / 折角 corner 用。
|
|
55
|
+
*
|
|
56
|
+
* 三态字符串语法(ADR-0004):
|
|
57
|
+
* - `'A'`(auto):节点中心;邻居端点 clip 时按"A 中心 → toward"射线求边界
|
|
58
|
+
* - `'A.<anchor>'`:命名 anchor 位置(位置即 endpoint,refPoint 也是它)
|
|
59
|
+
* - `'A.<deg>'`:节点 deg 方向上的视觉边界点(位置即 endpoint)
|
|
60
|
+
*
|
|
61
|
+
* 后两种"显式锚点"模式下 refPoint = endpoint(位置不随邻居变),auto 模式
|
|
62
|
+
* 下 refPoint 仍是中心。直接坐标 / 极坐标:解析后的笛卡尔。
|
|
63
|
+
*/
|
|
64
|
+
var refPointOfTarget = (target, nodeIndex) => {
|
|
65
|
+
if (typeof target === "string") {
|
|
66
|
+
const ref = require_parseTarget.parseNodeRef(target);
|
|
67
|
+
const node = nodeIndex.get(ref.id);
|
|
68
|
+
if (!node) return null;
|
|
69
|
+
switch (ref.kind) {
|
|
70
|
+
case "node": return [node.rect.x, node.rect.y];
|
|
71
|
+
case "anchor": return require_node.anchorOf(node, ref.anchor);
|
|
72
|
+
case "angle": return require_node.angleBoundaryOf(node, ref.angle);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return require_position.resolvePosition(target, nodeIndex);
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* 折角中间点:基于"参考点"(节点中心或直接坐标)算直角拐点。
|
|
79
|
+
* `-|` corner = (curr.x, prev.y);`|-` corner = (prev.x, curr.y)。
|
|
80
|
+
*/
|
|
81
|
+
var cornerOf = (prev, curr, via) => via === "-|" ? [curr[0], prev[1]] : [prev[0], curr[1]];
|
|
82
|
+
/**
|
|
83
|
+
* 把 step.to 在给定方向 `toward` 上算出"实际绘制端点":
|
|
84
|
+
* - 节点 ref auto(`'A'`):按 shape 多态走 boundaryPointOf——外扩 margin 后
|
|
85
|
+
* 求边界与"中心→toward"射线交点
|
|
86
|
+
* - 节点 ref 命名 anchor(`'A.north'`)/ 角度(`'A.30'`):位置已被解析定死,
|
|
87
|
+
* 直接返回,**不再受 toward 影响**
|
|
88
|
+
* - 直接坐标 / 极坐标:解析后直接返回(不做 clip)
|
|
89
|
+
* 解析失败返回 null。
|
|
90
|
+
*/
|
|
91
|
+
var clipForTarget = (target, toward, nodeIndex) => {
|
|
92
|
+
if (typeof target === "string") {
|
|
93
|
+
const ref = require_parseTarget.parseNodeRef(target);
|
|
94
|
+
const node = nodeIndex.get(ref.id);
|
|
95
|
+
if (!node) return null;
|
|
96
|
+
switch (ref.kind) {
|
|
97
|
+
case "node": return require_node.boundaryPointOf(node, toward);
|
|
98
|
+
case "anchor": return require_node.anchorOf(node, ref.anchor);
|
|
99
|
+
case "angle": return require_node.angleBoundaryOf(node, ref.angle);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return require_position.resolvePosition(target, nodeIndex);
|
|
103
|
+
};
|
|
104
|
+
/** 浅相等:两个 IRPosition 的两个分量都精确相等(未 round) */
|
|
105
|
+
var samePoint = (a, b) => !!a && !!b && a[0] === b[0] && a[1] === b[1];
|
|
12
106
|
/**
|
|
13
107
|
* 把 IR Path 翻译为单个 PathPrim。
|
|
14
|
-
*
|
|
15
|
-
* 1.
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
108
|
+
*
|
|
109
|
+
* 关键算法(v0.1.0-alpha.1):每个绘制段(line / fold)**独立**地用节点中心
|
|
110
|
+
* 算两端 boundary clip——一个节点在路径中段时,"入边"和"出边" boundary 点
|
|
111
|
+
* 通常不同,路径会在该节点处可见地"断开"。这与 TikZ 原生语义一致:
|
|
112
|
+
*
|
|
113
|
+
* `\draw (A) -- (B) -- (C);`
|
|
114
|
+
* 段 1:A.center → B.center 决定 A 出口、B 入口的 boundary 交点
|
|
115
|
+
* 段 2:B.center → C.center 决定 B 出口、C 入口的 boundary 交点
|
|
116
|
+
* B 在两段里 clip 出来的点不同——视觉上看到两条独立线段。
|
|
117
|
+
*
|
|
118
|
+
* 实现上仍只产一个 PathPrim:d 字符串里以多组 `M ... L ...` 表达多个 sub-path。
|
|
119
|
+
* 当某段起点恰好等于上一段终点(例如直接坐标连续,或未触发 clip 差异)时,
|
|
120
|
+
* 复用 cursor,省掉冗余 M。
|
|
121
|
+
*
|
|
122
|
+
* cycle 段:闭回最近一次 move 起点。若 cycle 起点 == lastEnd 且终点 == subPathStart,
|
|
123
|
+
* 输出 `Z`(最优雅);否则显式画一段 line(与"段独立 clip"一致)。
|
|
124
|
+
*
|
|
125
|
+
* 引用未定义节点 / 解析失败时返回 null(path 整体跳过)。
|
|
22
126
|
*/
|
|
23
127
|
var emitPathPrimitive = (path, nodeIndex, round) => {
|
|
24
128
|
const steps = path.children;
|
|
25
129
|
if (steps.length < 2) return null;
|
|
26
|
-
const
|
|
130
|
+
const anchors = steps.map((s) => s.kind === "cycle" ? null : refPointOfTarget(s.to, nodeIndex));
|
|
131
|
+
/** 找 i 之前最近的"有 to 字段的 step"(跳过 cycle) + 它的 anchor */
|
|
132
|
+
const findPrev = (i) => {
|
|
133
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
134
|
+
const s = steps[j];
|
|
135
|
+
if (s.kind === "cycle") continue;
|
|
136
|
+
const a = anchors[j];
|
|
137
|
+
if (!a) return null;
|
|
138
|
+
return {
|
|
139
|
+
step: s,
|
|
140
|
+
anchor: a
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
};
|
|
145
|
+
/** 找 i 之前最近的 move 的 to——cycle 闭合的目标 */
|
|
146
|
+
const findRecentMoveTo = (i) => {
|
|
147
|
+
for (let j = i - 1; j >= 0; j--) if (steps[j].kind === "move") return steps[j].to;
|
|
148
|
+
return null;
|
|
149
|
+
};
|
|
150
|
+
const ops = [];
|
|
27
151
|
const points = [];
|
|
152
|
+
let lastEnd = null;
|
|
153
|
+
let subPathStart = null;
|
|
154
|
+
const emitM = (p) => {
|
|
155
|
+
ops.push({
|
|
156
|
+
cmd: "M",
|
|
157
|
+
point: p
|
|
158
|
+
});
|
|
159
|
+
points.push(p);
|
|
160
|
+
subPathStart = p;
|
|
161
|
+
lastEnd = p;
|
|
162
|
+
};
|
|
163
|
+
const emitL = (p) => {
|
|
164
|
+
ops.push({
|
|
165
|
+
cmd: "L",
|
|
166
|
+
point: p
|
|
167
|
+
});
|
|
168
|
+
points.push(p);
|
|
169
|
+
lastEnd = p;
|
|
170
|
+
};
|
|
171
|
+
const emitZ = () => {
|
|
172
|
+
ops.push({ cmd: "Z" });
|
|
173
|
+
lastEnd = subPathStart;
|
|
174
|
+
};
|
|
175
|
+
/** 段起点:与 lastEnd 相同就复用 cursor(省掉冗余 M),否则发 M */
|
|
176
|
+
const startSegment = (p) => {
|
|
177
|
+
if (samePoint(p, lastEnd)) return;
|
|
178
|
+
emitM(p);
|
|
179
|
+
};
|
|
28
180
|
for (let i = 0; i < steps.length; i++) {
|
|
29
|
-
const
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
if (!
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
181
|
+
const step = steps[i];
|
|
182
|
+
if (step.kind === "move") continue;
|
|
183
|
+
if (step.kind === "cycle") {
|
|
184
|
+
const moveTo = findRecentMoveTo(i);
|
|
185
|
+
const prev = findPrev(i);
|
|
186
|
+
if (!moveTo || !prev) continue;
|
|
187
|
+
const moveAnchor = refPointOfTarget(moveTo, nodeIndex);
|
|
188
|
+
if (!moveAnchor) return null;
|
|
189
|
+
const fromClip = clipForTarget(prev.step.to, moveAnchor, nodeIndex);
|
|
190
|
+
const toClip = clipForTarget(moveTo, prev.anchor, nodeIndex);
|
|
191
|
+
if (!fromClip || !toClip) return null;
|
|
192
|
+
if (samePoint(fromClip, lastEnd) && samePoint(toClip, subPathStart)) {
|
|
193
|
+
emitZ();
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
startSegment(fromClip);
|
|
197
|
+
emitL(toClip);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
const prev = findPrev(i);
|
|
201
|
+
if (!prev) return null;
|
|
202
|
+
const currAnchor = anchors[i];
|
|
203
|
+
if (!currAnchor) return null;
|
|
204
|
+
if (step.kind === "line") {
|
|
205
|
+
const fromClip = clipForTarget(prev.step.to, currAnchor, nodeIndex);
|
|
206
|
+
const toClip = clipForTarget(step.to, prev.anchor, nodeIndex);
|
|
207
|
+
if (!fromClip || !toClip) return null;
|
|
208
|
+
startSegment(fromClip);
|
|
209
|
+
emitL(toClip);
|
|
210
|
+
continue;
|
|
40
211
|
}
|
|
212
|
+
const corner = cornerOf(prev.anchor, currAnchor, step.via);
|
|
213
|
+
const fromClip = clipForTarget(prev.step.to, corner, nodeIndex);
|
|
214
|
+
const toClip = clipForTarget(step.to, corner, nodeIndex);
|
|
215
|
+
if (!fromClip || !toClip) return null;
|
|
216
|
+
startSegment(fromClip);
|
|
217
|
+
emitL(corner);
|
|
218
|
+
emitL(toClip);
|
|
41
219
|
}
|
|
42
|
-
|
|
220
|
+
const strokeWidth = path.strokeWidth ?? 1;
|
|
221
|
+
const baseProps = {
|
|
222
|
+
stroke: path.stroke ?? "currentColor",
|
|
223
|
+
strokeWidth,
|
|
224
|
+
fill: path.fill ?? "none",
|
|
225
|
+
fillRule: path.fillRule,
|
|
226
|
+
strokeDasharray: path.strokeDasharray
|
|
227
|
+
};
|
|
228
|
+
const markers = arrowMarkers(path.arrow, path.arrowShape);
|
|
229
|
+
const hasArrows = !!markers.arrowStart || !!markers.arrowEnd;
|
|
230
|
+
const shrinkStart = markers.arrowStart ? SHRINK_FOR_SHAPE[markers.arrowStart] : 0;
|
|
231
|
+
const shrinkEnd = markers.arrowEnd ? SHRINK_FOR_SHAPE[markers.arrowEnd] : 0;
|
|
232
|
+
if (shrinkStart > 0) {
|
|
233
|
+
const firstIdx = ops.findIndex((o) => o.cmd === "M");
|
|
234
|
+
if (firstIdx >= 0) {
|
|
235
|
+
const cur = ops[firstIdx];
|
|
236
|
+
const next = ops.slice(firstIdx + 1).find((o) => o.cmd !== "Z");
|
|
237
|
+
if (cur.cmd !== "Z" && next) cur.point = shiftToward(cur.point, next.point, shrinkStart * strokeWidth);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (shrinkEnd > 0) {
|
|
241
|
+
let lastIdx = -1;
|
|
242
|
+
for (let i = ops.length - 1; i >= 0; i--) if (ops[i].cmd !== "Z") {
|
|
243
|
+
lastIdx = i;
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
if (lastIdx > 0) {
|
|
247
|
+
let prevIdx = lastIdx - 1;
|
|
248
|
+
while (prevIdx >= 0 && ops[prevIdx].cmd === "Z") prevIdx--;
|
|
249
|
+
if (prevIdx >= 0) {
|
|
250
|
+
const cur = ops[lastIdx];
|
|
251
|
+
const prev = ops[prevIdx];
|
|
252
|
+
if (cur.cmd !== "Z" && prev.cmd !== "Z") cur.point = shiftToward(cur.point, prev.point, shrinkEnd * strokeWidth);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
const tokens = ops.map((op) => {
|
|
257
|
+
if (op.cmd === "Z") return "Z";
|
|
258
|
+
return `${op.cmd} ${round(op.point[0])} ${round(op.point[1])}`;
|
|
259
|
+
});
|
|
260
|
+
const subPathStarts = [];
|
|
261
|
+
tokens.forEach((tok, idx) => {
|
|
262
|
+
if (tok.startsWith("M ")) subPathStarts.push(idx);
|
|
263
|
+
});
|
|
264
|
+
if (!hasArrows || subPathStarts.length <= 1) return {
|
|
43
265
|
primitive: {
|
|
44
266
|
type: "path",
|
|
45
|
-
d:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
267
|
+
d: tokens.join(" "),
|
|
268
|
+
...baseProps,
|
|
269
|
+
...markers
|
|
270
|
+
},
|
|
271
|
+
points
|
|
272
|
+
};
|
|
273
|
+
const subPathSlices = [];
|
|
274
|
+
for (let s = 0; s < subPathStarts.length; s++) {
|
|
275
|
+
const start = subPathStarts[s];
|
|
276
|
+
const end = s + 1 < subPathStarts.length ? subPathStarts[s + 1] : tokens.length;
|
|
277
|
+
subPathSlices.push(tokens.slice(start, end));
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
primitive: {
|
|
281
|
+
type: "group",
|
|
282
|
+
children: subPathSlices.map((sub, i) => {
|
|
283
|
+
const isFirst = i === 0;
|
|
284
|
+
const isLast = i === subPathSlices.length - 1;
|
|
285
|
+
return {
|
|
286
|
+
type: "path",
|
|
287
|
+
d: sub.join(" "),
|
|
288
|
+
...baseProps,
|
|
289
|
+
...isFirst && markers.arrowStart ? { arrowStart: markers.arrowStart } : {},
|
|
290
|
+
...isLast && markers.arrowEnd ? { arrowEnd: markers.arrowEnd } : {}
|
|
291
|
+
};
|
|
292
|
+
})
|
|
50
293
|
},
|
|
51
294
|
points
|
|
52
295
|
};
|
|
@@ -3,14 +3,24 @@ import { ScenePrimitive } from '../primitive';
|
|
|
3
3
|
import { NodeLayout } from './node';
|
|
4
4
|
/**
|
|
5
5
|
* 把 IR Path 翻译为单个 PathPrim。
|
|
6
|
-
* 算法:
|
|
7
|
-
* 1. 第一遍:把每个 step.to 解析为"参考点"(节点中心 / 直接坐标 / 极坐标解算后),
|
|
8
|
-
* 用于给邻居 step 算节点边界点的方向;
|
|
9
|
-
* 2. 第二遍:算每个 step 的实际终点——节点引用调用 boundaryPoint 贴 attachRect;
|
|
10
|
-
* 其他形态直接用解析后的笛卡尔。
|
|
11
|
-
* 3. 把点序列写成 SVG path d 字符串。
|
|
12
6
|
*
|
|
13
|
-
*
|
|
7
|
+
* 关键算法(v0.1.0-alpha.1):每个绘制段(line / fold)**独立**地用节点中心
|
|
8
|
+
* 算两端 boundary clip——一个节点在路径中段时,"入边"和"出边" boundary 点
|
|
9
|
+
* 通常不同,路径会在该节点处可见地"断开"。这与 TikZ 原生语义一致:
|
|
10
|
+
*
|
|
11
|
+
* `\draw (A) -- (B) -- (C);`
|
|
12
|
+
* 段 1:A.center → B.center 决定 A 出口、B 入口的 boundary 交点
|
|
13
|
+
* 段 2:B.center → C.center 决定 B 出口、C 入口的 boundary 交点
|
|
14
|
+
* B 在两段里 clip 出来的点不同——视觉上看到两条独立线段。
|
|
15
|
+
*
|
|
16
|
+
* 实现上仍只产一个 PathPrim:d 字符串里以多组 `M ... L ...` 表达多个 sub-path。
|
|
17
|
+
* 当某段起点恰好等于上一段终点(例如直接坐标连续,或未触发 clip 差异)时,
|
|
18
|
+
* 复用 cursor,省掉冗余 M。
|
|
19
|
+
*
|
|
20
|
+
* cycle 段:闭回最近一次 move 起点。若 cycle 起点 == lastEnd 且终点 == subPathStart,
|
|
21
|
+
* 输出 `Z`(最优雅);否则显式画一段 line(与"段独立 clip"一致)。
|
|
22
|
+
*
|
|
23
|
+
* 引用未定义节点 / 解析失败时返回 null(path 整体跳过)。
|
|
14
24
|
*/
|
|
15
25
|
export declare const emitPathPrimitive: (path: IRPath, nodeIndex: Map<string, NodeLayout>, round: (n: number) => number) => {
|
|
16
26
|
primitive: ScenePrimitive;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../../src/compile/path.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../../src/compile/path.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,MAAM,EAAE,UAAU,EAAoB,MAAM,OAAO,CAAC;AAC9E,OAAO,KAAK,EAAY,cAAc,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,KAAK,UAAU,EAA8C,MAAM,QAAQ,CAAC;AAuIrF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iBAAiB,GAC5B,MAAM,MAAM,EACZ,WAAW,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EAClC,OAAO,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,KAC3B;IAAE,SAAS,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAA;CAAE,GAAG,IAsN7D,CAAC"}
|
|
@@ -6,8 +6,8 @@ export type FontSpec = {
|
|
|
6
6
|
size: number;
|
|
7
7
|
/** 字重;可以是 'normal' / 'bold' / 100~900 数字等 */
|
|
8
8
|
weight?: string | number;
|
|
9
|
-
/** 字形:normal
|
|
10
|
-
style?: 'normal' | 'italic';
|
|
9
|
+
/** 字形:normal / italic / oblique */
|
|
10
|
+
style?: 'normal' | 'italic' | 'oblique';
|
|
11
11
|
};
|
|
12
12
|
/** 文字度量结果:宽高 + 可选的基线 ascent/descent */
|
|
13
13
|
export type TextMetrics = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text-metrics.d.ts","sourceRoot":"","sources":["../../../src/compile/text-metrics.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,MAAM,MAAM,QAAQ,GAAG;IACrB,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,
|
|
1
|
+
{"version":3,"file":"text-metrics.d.ts","sourceRoot":"","sources":["../../../src/compile/text-metrics.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,MAAM,MAAM,QAAQ,GAAG;IACrB,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,mCAAmC;IACnC,KAAK,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;CACzC,CAAC;AAEF,uCAAuC;AACvC,MAAM,MAAM,WAAW,GAAG;IACxB,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,KAAK,WAAW,CAAC;AAEzE,uCAAuC;AACvC,eAAO,MAAM,gBAAgB,EAAE,YAG7B,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
//#region src/geometry/circle.ts
|
|
2
|
+
var SQRT_HALF = Math.SQRT1_2;
|
|
3
|
+
var localToWorld = (c, local) => {
|
|
4
|
+
const angle = c.rotate ?? 0;
|
|
5
|
+
if (angle === 0) return [c.x + local[0], c.y + local[1]];
|
|
6
|
+
const cos = Math.cos(angle);
|
|
7
|
+
const sin = Math.sin(angle);
|
|
8
|
+
return [c.x + local[0] * cos - local[1] * sin, c.y + local[0] * sin + local[1] * cos];
|
|
9
|
+
};
|
|
10
|
+
var worldToLocal = (c, world) => {
|
|
11
|
+
const tx = world[0] - c.x;
|
|
12
|
+
const ty = world[1] - c.y;
|
|
13
|
+
const angle = c.rotate ?? 0;
|
|
14
|
+
if (angle === 0) return [tx, ty];
|
|
15
|
+
const cos = Math.cos(angle);
|
|
16
|
+
const sin = Math.sin(angle);
|
|
17
|
+
return [tx * cos + ty * sin, -tx * sin + ty * cos];
|
|
18
|
+
};
|
|
19
|
+
/** 圆形相关基础工具 */
|
|
20
|
+
var circle = {
|
|
21
|
+
/** 圆心 */
|
|
22
|
+
center: (c) => [c.x, c.y],
|
|
23
|
+
/** 判断点是否在圆内(含边界) */
|
|
24
|
+
contains: (c, p) => {
|
|
25
|
+
const [lx, ly] = worldToLocal(c, p);
|
|
26
|
+
return lx * lx + ly * ly <= c.radius * c.radius;
|
|
27
|
+
},
|
|
28
|
+
/** 9 个标准 anchor 之一的世界坐标 */
|
|
29
|
+
anchor: (c, name) => {
|
|
30
|
+
const r = c.radius;
|
|
31
|
+
let lx = 0;
|
|
32
|
+
let ly = 0;
|
|
33
|
+
switch (name) {
|
|
34
|
+
case "center": break;
|
|
35
|
+
case "north":
|
|
36
|
+
ly = -r;
|
|
37
|
+
break;
|
|
38
|
+
case "south":
|
|
39
|
+
ly = r;
|
|
40
|
+
break;
|
|
41
|
+
case "east":
|
|
42
|
+
lx = r;
|
|
43
|
+
break;
|
|
44
|
+
case "west":
|
|
45
|
+
lx = -r;
|
|
46
|
+
break;
|
|
47
|
+
case "north-east":
|
|
48
|
+
lx = r * SQRT_HALF;
|
|
49
|
+
ly = -r * SQRT_HALF;
|
|
50
|
+
break;
|
|
51
|
+
case "north-west":
|
|
52
|
+
lx = -r * SQRT_HALF;
|
|
53
|
+
ly = -r * SQRT_HALF;
|
|
54
|
+
break;
|
|
55
|
+
case "south-east":
|
|
56
|
+
lx = r * SQRT_HALF;
|
|
57
|
+
ly = r * SQRT_HALF;
|
|
58
|
+
break;
|
|
59
|
+
case "south-west":
|
|
60
|
+
lx = -r * SQRT_HALF;
|
|
61
|
+
ly = r * SQRT_HALF;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
return localToWorld(c, [lx, ly]);
|
|
65
|
+
},
|
|
66
|
+
/**
|
|
67
|
+
* 从圆心向 toward 方向画射线,求与圆周的交点。
|
|
68
|
+
* 用于把 Path 端点贴到 Node 边界。
|
|
69
|
+
*/
|
|
70
|
+
boundaryPoint: (c, toward) => {
|
|
71
|
+
const [lx, ly] = worldToLocal(c, toward);
|
|
72
|
+
const len = Math.sqrt(lx * lx + ly * ly);
|
|
73
|
+
if (len === 0) return [c.x, c.y];
|
|
74
|
+
const t = c.radius / len;
|
|
75
|
+
return localToWorld(c, [lx * t, ly * t]);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
//#endregion
|
|
79
|
+
exports.circle = circle;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Position } from './point';
|
|
2
|
+
/** 圆形:几何中心 (x, y) + 半径;与 Rect 一样支持可选旋转(圆对自身旋转无影响,预留供 anchor 命名一致性) */
|
|
3
|
+
export type Circle = {
|
|
4
|
+
/** 圆心横坐标 */
|
|
5
|
+
x: number;
|
|
6
|
+
/** 圆心纵坐标 */
|
|
7
|
+
y: number;
|
|
8
|
+
/** 半径(user units) */
|
|
9
|
+
radius: number;
|
|
10
|
+
/** 绕几何中心旋转弧度;保留与 Rect 同形 API,圆形本身旋转后视觉不变 */
|
|
11
|
+
rotate?: number;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* 圆形 9 个标准 anchor 名集合,与 RECT_ANCHORS 同名同义。
|
|
15
|
+
* 圆的"4 方位 + 4 对角"在圆周上等距分布(每 45°)。
|
|
16
|
+
*/
|
|
17
|
+
export type CircleAnchor = 'center' | 'north' | 'south' | 'east' | 'west' | 'north-east' | 'north-west' | 'south-east' | 'south-west';
|
|
18
|
+
/** 圆形相关基础工具 */
|
|
19
|
+
export declare const circle: {
|
|
20
|
+
/** 圆心 */
|
|
21
|
+
center: (c: Circle) => Position;
|
|
22
|
+
/** 判断点是否在圆内(含边界) */
|
|
23
|
+
contains: (c: Circle, p: Position) => boolean;
|
|
24
|
+
/** 9 个标准 anchor 之一的世界坐标 */
|
|
25
|
+
anchor: (c: Circle, name: CircleAnchor) => Position;
|
|
26
|
+
/**
|
|
27
|
+
* 从圆心向 toward 方向画射线,求与圆周的交点。
|
|
28
|
+
* 用于把 Path 端点贴到 Node 边界。
|
|
29
|
+
*/
|
|
30
|
+
boundaryPoint: (c: Circle, toward: Position) => Position;
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=circle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circle.d.ts","sourceRoot":"","sources":["../../../src/geometry/circle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,sEAAsE;AACtE,MAAM,MAAM,MAAM,GAAG;IACnB,YAAY;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,YAAY;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,qBAAqB;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAIF;;;GAGG;AACH,MAAM,MAAM,YAAY,GACpB,QAAQ,GACR,OAAO,GACP,OAAO,GACP,MAAM,GACN,MAAM,GACN,YAAY,GACZ,YAAY,GACZ,YAAY,GACZ,YAAY,CAAC;AAoBjB,eAAe;AACf,eAAO,MAAM,MAAM;IACjB,SAAS;gBACG,MAAM,KAAG,QAAQ;IAC7B,oBAAoB;kBACN,MAAM,KAAK,QAAQ,KAAG,OAAO;IAI3C,2BAA2B;gBACf,MAAM,QAAQ,YAAY,KAAG,QAAQ;IAsCjD;;;OAGG;uBACgB,MAAM,UAAU,QAAQ,KAAG,QAAQ;CAOvD,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
//#region src/geometry/diamond.ts
|
|
2
|
+
var localToWorld = (d, local) => {
|
|
3
|
+
const angle = d.rotate ?? 0;
|
|
4
|
+
if (angle === 0) return [d.x + local[0], d.y + local[1]];
|
|
5
|
+
const cos = Math.cos(angle);
|
|
6
|
+
const sin = Math.sin(angle);
|
|
7
|
+
return [d.x + local[0] * cos - local[1] * sin, d.y + local[0] * sin + local[1] * cos];
|
|
8
|
+
};
|
|
9
|
+
var worldToLocal = (d, world) => {
|
|
10
|
+
const tx = world[0] - d.x;
|
|
11
|
+
const ty = world[1] - d.y;
|
|
12
|
+
const angle = d.rotate ?? 0;
|
|
13
|
+
if (angle === 0) return [tx, ty];
|
|
14
|
+
const cos = Math.cos(angle);
|
|
15
|
+
const sin = Math.sin(angle);
|
|
16
|
+
return [tx * cos + ty * sin, -tx * sin + ty * cos];
|
|
17
|
+
};
|
|
18
|
+
/** 菱形相关基础工具 */
|
|
19
|
+
var diamond = {
|
|
20
|
+
/** 中心 */
|
|
21
|
+
center: (d) => [d.x, d.y],
|
|
22
|
+
/**
|
|
23
|
+
* 判断点是否在菱形内(含边界,考虑旋转)。
|
|
24
|
+
* 菱形方程:|x|/halfA + |y|/halfB ≤ 1。
|
|
25
|
+
*/
|
|
26
|
+
contains: (d, p) => {
|
|
27
|
+
const [lx, ly] = worldToLocal(d, p);
|
|
28
|
+
return Math.abs(lx) / d.halfA + Math.abs(ly) / d.halfB <= 1.000000001;
|
|
29
|
+
},
|
|
30
|
+
/**
|
|
31
|
+
* 9 个标准 anchor 的世界坐标。
|
|
32
|
+
* - N/S/E/W:四个顶点
|
|
33
|
+
* - NE/NW/SE/SW:四条边的中点
|
|
34
|
+
* - center:几何中心
|
|
35
|
+
*/
|
|
36
|
+
anchor: (d, name) => {
|
|
37
|
+
let lx = 0;
|
|
38
|
+
let ly = 0;
|
|
39
|
+
switch (name) {
|
|
40
|
+
case "center": break;
|
|
41
|
+
case "north":
|
|
42
|
+
ly = -d.halfB;
|
|
43
|
+
break;
|
|
44
|
+
case "south":
|
|
45
|
+
ly = d.halfB;
|
|
46
|
+
break;
|
|
47
|
+
case "east":
|
|
48
|
+
lx = d.halfA;
|
|
49
|
+
break;
|
|
50
|
+
case "west":
|
|
51
|
+
lx = -d.halfA;
|
|
52
|
+
break;
|
|
53
|
+
case "north-east":
|
|
54
|
+
lx = d.halfA / 2;
|
|
55
|
+
ly = -d.halfB / 2;
|
|
56
|
+
break;
|
|
57
|
+
case "north-west":
|
|
58
|
+
lx = -d.halfA / 2;
|
|
59
|
+
ly = -d.halfB / 2;
|
|
60
|
+
break;
|
|
61
|
+
case "south-east":
|
|
62
|
+
lx = d.halfA / 2;
|
|
63
|
+
ly = d.halfB / 2;
|
|
64
|
+
break;
|
|
65
|
+
case "south-west":
|
|
66
|
+
lx = -d.halfA / 2;
|
|
67
|
+
ly = d.halfB / 2;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
return localToWorld(d, [lx, ly]);
|
|
71
|
+
},
|
|
72
|
+
/**
|
|
73
|
+
* 从中心向 toward 方向画射线,求与菱形 4 条边之一的交点。
|
|
74
|
+
* 菱形方程:|x|/halfA + |y|/halfB = 1。
|
|
75
|
+
* 沿方向 (lx, ly) 缩放 t 倍命中:t × (|lx|/halfA + |ly|/halfB) = 1
|
|
76
|
+
* → t = 1 / (|lx|/halfA + |ly|/halfB)。
|
|
77
|
+
*/
|
|
78
|
+
boundaryPoint: (d, toward) => {
|
|
79
|
+
const [lx, ly] = worldToLocal(d, toward);
|
|
80
|
+
const denom = Math.abs(lx) / d.halfA + Math.abs(ly) / d.halfB;
|
|
81
|
+
if (denom === 0) return [d.x, d.y];
|
|
82
|
+
const t = 1 / denom;
|
|
83
|
+
return localToWorld(d, [lx * t, ly * t]);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
//#endregion
|
|
87
|
+
exports.diamond = diamond;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Position } from './point';
|
|
2
|
+
/**
|
|
3
|
+
* 菱形:几何中心 (x, y) + 半轴长 halfA(水平)/ halfB(垂直)+ 可选旋转。
|
|
4
|
+
* 4 个顶点位于 (±halfA, 0) 与 (0, ±halfB),4 条边界线把这些顶点两两相连。
|
|
5
|
+
*/
|
|
6
|
+
export type Diamond = {
|
|
7
|
+
/** 中心横坐标 */
|
|
8
|
+
x: number;
|
|
9
|
+
/** 中心纵坐标 */
|
|
10
|
+
y: number;
|
|
11
|
+
/** 水平半轴长(中心到 east/west 顶点的距离) */
|
|
12
|
+
halfA: number;
|
|
13
|
+
/** 垂直半轴长(中心到 north/south 顶点的距离) */
|
|
14
|
+
halfB: number;
|
|
15
|
+
/** 绕几何中心旋转弧度;省略或 0 表示不旋转 */
|
|
16
|
+
rotate?: number;
|
|
17
|
+
};
|
|
18
|
+
/** 菱形 9 个标准 anchor 名(4 顶点 + 4 边中点 + 中心) */
|
|
19
|
+
export type DiamondAnchor = 'center' | 'north' | 'south' | 'east' | 'west' | 'north-east' | 'north-west' | 'south-east' | 'south-west';
|
|
20
|
+
/** 菱形相关基础工具 */
|
|
21
|
+
export declare const diamond: {
|
|
22
|
+
/** 中心 */
|
|
23
|
+
center: (d: Diamond) => Position;
|
|
24
|
+
/**
|
|
25
|
+
* 判断点是否在菱形内(含边界,考虑旋转)。
|
|
26
|
+
* 菱形方程:|x|/halfA + |y|/halfB ≤ 1。
|
|
27
|
+
*/
|
|
28
|
+
contains: (d: Diamond, p: Position) => boolean;
|
|
29
|
+
/**
|
|
30
|
+
* 9 个标准 anchor 的世界坐标。
|
|
31
|
+
* - N/S/E/W:四个顶点
|
|
32
|
+
* - NE/NW/SE/SW:四条边的中点
|
|
33
|
+
* - center:几何中心
|
|
34
|
+
*/
|
|
35
|
+
anchor: (d: Diamond, name: DiamondAnchor) => Position;
|
|
36
|
+
/**
|
|
37
|
+
* 从中心向 toward 方向画射线,求与菱形 4 条边之一的交点。
|
|
38
|
+
* 菱形方程:|x|/halfA + |y|/halfB = 1。
|
|
39
|
+
* 沿方向 (lx, ly) 缩放 t 倍命中:t × (|lx|/halfA + |ly|/halfB) = 1
|
|
40
|
+
* → t = 1 / (|lx|/halfA + |ly|/halfB)。
|
|
41
|
+
*/
|
|
42
|
+
boundaryPoint: (d: Diamond, toward: Position) => Position;
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=diamond.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diamond.d.ts","sourceRoot":"","sources":["../../../src/geometry/diamond.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC;;;GAGG;AACH,MAAM,MAAM,OAAO,GAAG;IACpB,YAAY;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,YAAY;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,4BAA4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,2CAA2C;AAC3C,MAAM,MAAM,aAAa,GACrB,QAAQ,GACR,OAAO,GACP,OAAO,GACP,MAAM,GACN,MAAM,GACN,YAAY,GACZ,YAAY,GACZ,YAAY,GACZ,YAAY,CAAC;AAoBjB,eAAe;AACf,eAAO,MAAM,OAAO;IAClB,SAAS;gBACG,OAAO,KAAG,QAAQ;IAC9B;;;OAGG;kBACW,OAAO,KAAK,QAAQ,KAAG,OAAO;IAI5C;;;;;OAKG;gBACS,OAAO,QAAQ,aAAa,KAAG,QAAQ;IAqCnD;;;;;OAKG;uBACgB,OAAO,UAAU,QAAQ,KAAG,QAAQ;CAOxD,CAAC"}
|