@retikz/core 0.2.0-alpha.1 → 0.2.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/compile/compile.d.ts +8 -1
- package/dist/es/compile/compile.d.ts.map +1 -1
- package/dist/es/compile/compile.js +21 -6
- package/dist/es/compile/node.d.ts +15 -10
- package/dist/es/compile/node.d.ts.map +1 -1
- package/dist/es/compile/node.js +44 -181
- package/dist/es/compile/path/index.js +1 -1
- package/dist/es/compile/path/label.d.ts +1 -1
- package/dist/es/compile/path/label.d.ts.map +1 -1
- package/dist/es/compile/path/label.js +17 -4
- package/dist/es/compile/scope.d.ts.map +1 -1
- package/dist/es/compile/scope.js +3 -1
- package/dist/es/compile/style.d.ts +46 -0
- package/dist/es/compile/style.d.ts.map +1 -0
- package/dist/es/compile/style.js +259 -0
- package/dist/es/index.d.ts +4 -2
- package/dist/es/index.d.ts.map +1 -1
- package/dist/es/index.js +5 -3
- package/dist/es/ir/node.d.ts +19 -12
- package/dist/es/ir/node.d.ts.map +1 -1
- package/dist/es/ir/node.js +2 -1
- package/dist/es/ir/path/path.d.ts +531 -0
- package/dist/es/ir/path/path.d.ts.map +1 -1
- package/dist/es/ir/path/path.js +1 -0
- package/dist/es/ir/path/step.d.ts +834 -0
- package/dist/es/ir/path/step.d.ts.map +1 -1
- package/dist/es/ir/path/step.js +5 -1
- package/dist/es/ir/scope.d.ts +3493 -3
- package/dist/es/ir/scope.d.ts.map +1 -1
- package/dist/es/ir/scope.js +64 -4
- package/dist/es/shapes/_shared.d.ts +7 -0
- package/dist/es/shapes/_shared.d.ts.map +1 -0
- package/dist/es/shapes/_shared.js +11 -0
- package/dist/es/shapes/circle.d.ts +8 -0
- package/dist/es/shapes/circle.d.ts.map +1 -0
- package/dist/es/shapes/circle.js +33 -0
- package/dist/es/shapes/diamond.d.ts +8 -0
- package/dist/es/shapes/diamond.d.ts.map +1 -0
- package/dist/es/shapes/diamond.js +65 -0
- package/dist/es/shapes/ellipse.d.ts +8 -0
- package/dist/es/shapes/ellipse.d.ts.map +1 -0
- package/dist/es/shapes/ellipse.js +45 -0
- package/dist/es/shapes/index.d.ts +14 -0
- package/dist/es/shapes/index.d.ts.map +1 -0
- package/dist/es/shapes/index.js +15 -0
- package/dist/es/shapes/rectangle.d.ts +8 -0
- package/dist/es/shapes/rectangle.d.ts.map +1 -0
- package/dist/es/shapes/rectangle.js +40 -0
- package/dist/es/shapes/types.d.ts +44 -0
- package/dist/es/shapes/types.d.ts.map +1 -0
- package/dist/lib/compile/compile.cjs +23 -8
- package/dist/lib/compile/compile.d.ts +8 -1
- package/dist/lib/compile/compile.d.ts.map +1 -1
- package/dist/lib/compile/node.cjs +44 -181
- package/dist/lib/compile/node.d.ts +15 -10
- package/dist/lib/compile/node.d.ts.map +1 -1
- package/dist/lib/compile/path/index.cjs +1 -1
- package/dist/lib/compile/path/label.cjs +17 -4
- package/dist/lib/compile/path/label.d.ts +1 -1
- package/dist/lib/compile/path/label.d.ts.map +1 -1
- package/dist/lib/compile/scope.cjs +3 -1
- package/dist/lib/compile/scope.d.ts.map +1 -1
- package/dist/lib/compile/style.cjs +262 -0
- package/dist/lib/compile/style.d.ts +46 -0
- package/dist/lib/compile/style.d.ts.map +1 -0
- package/dist/lib/index.cjs +10 -1
- package/dist/lib/index.d.ts +4 -2
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/ir/node.cjs +2 -1
- package/dist/lib/ir/node.d.ts +19 -12
- package/dist/lib/ir/node.d.ts.map +1 -1
- package/dist/lib/ir/path/path.cjs +1 -0
- package/dist/lib/ir/path/path.d.ts +531 -0
- package/dist/lib/ir/path/path.d.ts.map +1 -1
- package/dist/lib/ir/path/step.cjs +5 -1
- package/dist/lib/ir/path/step.d.ts +834 -0
- package/dist/lib/ir/path/step.d.ts.map +1 -1
- package/dist/lib/ir/scope.cjs +67 -3
- package/dist/lib/ir/scope.d.ts +3493 -3
- package/dist/lib/ir/scope.d.ts.map +1 -1
- package/dist/lib/shapes/_shared.cjs +11 -0
- package/dist/lib/shapes/_shared.d.ts +7 -0
- package/dist/lib/shapes/_shared.d.ts.map +1 -0
- package/dist/lib/shapes/circle.cjs +33 -0
- package/dist/lib/shapes/circle.d.ts +8 -0
- package/dist/lib/shapes/circle.d.ts.map +1 -0
- package/dist/lib/shapes/diamond.cjs +65 -0
- package/dist/lib/shapes/diamond.d.ts +8 -0
- package/dist/lib/shapes/diamond.d.ts.map +1 -0
- package/dist/lib/shapes/ellipse.cjs +45 -0
- package/dist/lib/shapes/ellipse.d.ts +8 -0
- package/dist/lib/shapes/ellipse.d.ts.map +1 -0
- package/dist/lib/shapes/index.cjs +14 -0
- package/dist/lib/shapes/index.d.ts +14 -0
- package/dist/lib/shapes/index.d.ts.map +1 -0
- package/dist/lib/shapes/rectangle.cjs +40 -0
- package/dist/lib/shapes/rectangle.d.ts +8 -0
- package/dist/lib/shapes/rectangle.d.ts.map +1 -0
- package/dist/lib/shapes/types.d.ts +44 -0
- package/dist/lib/shapes/types.d.ts.map +1 -0
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IR } from '../ir';
|
|
2
2
|
import { Scene } from '../primitive';
|
|
3
|
+
import { ShapeDefinition } from '../shapes';
|
|
3
4
|
import { TextMeasurer } from './text-metrics';
|
|
4
5
|
/** 编译期警告:path / position 解析失败时通过 `CompileOptions.onWarn` 发出,不影响编译产物 */
|
|
5
6
|
export type CompileWarning = {
|
|
@@ -7,7 +8,7 @@ export type CompileWarning = {
|
|
|
7
8
|
* 警告类型代码(机器可读)
|
|
8
9
|
* @description 用户可按 code 分支处理;未来 alpha 加新 code 不破坏调用方
|
|
9
10
|
*/
|
|
10
|
-
code: 'UNRESOLVED_NODE_REFERENCE' | 'PATH_TOO_SHORT' | 'ANCHOR_RESOLUTION_FAILED' | 'OFFSET_BASE_UNRESOLVED' | 'POLAR_ORIGIN_UNRESOLVED' | 'AT_TARGET_UNRESOLVED' | 'RELATIVE_INITIAL_NO_PREV_END' | 'BBOX_EXTREME_INPUT' | 'DUPLICATE_NODE_ID' | (string & {});
|
|
11
|
+
code: 'UNRESOLVED_NODE_REFERENCE' | 'PATH_TOO_SHORT' | 'ANCHOR_RESOLUTION_FAILED' | 'OFFSET_BASE_UNRESOLVED' | 'POLAR_ORIGIN_UNRESOLVED' | 'AT_TARGET_UNRESOLVED' | 'RELATIVE_INITIAL_NO_PREV_END' | 'BBOX_EXTREME_INPUT' | 'DUPLICATE_NODE_ID' | 'SHAPE_OVERRIDES_BUILTIN' | (string & {});
|
|
11
12
|
/** 人类可读消息(英文) */
|
|
12
13
|
message: string;
|
|
13
14
|
/** IR locator 路径(jq-like,如 `'children[3].path.children[1].to'`) */
|
|
@@ -34,6 +35,12 @@ export type CompileOptions = {
|
|
|
34
35
|
* @description path / position 解析失败时按 IR locator + code + message 同步触发;不传时 dev 模式(`process.env.NODE_ENV !== 'production'`)默认 `console.warn`、生产静默
|
|
35
36
|
*/
|
|
36
37
|
onWarn?: (warning: CompileWarning) => void;
|
|
38
|
+
/**
|
|
39
|
+
* 运行时注入的第三方 shape(不进 IR)
|
|
40
|
+
* @description 有效 shape 表 = `{ ...BUILTIN_SHAPES, ...shapes }`——同名 key 覆盖内置,经 `onWarn` 发
|
|
41
|
+
* `SHAPE_OVERRIDES_BUILTIN`。IR 的 `node.shape` 仍是字符串;未注册名在编译期 throw。
|
|
42
|
+
*/
|
|
43
|
+
shapes?: Record<string, ShapeDefinition>;
|
|
37
44
|
};
|
|
38
45
|
/**
|
|
39
46
|
* IR → Scene 纯函数转换,所有 adapter 共享
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../../src/compile/compile.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAwC,MAAM,OAAO,CAAC;AACtE,OAAO,KAAK,EAAa,KAAK,EAA6B,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../../src/compile/compile.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAwC,MAAM,OAAO,CAAC;AACtE,OAAO,KAAK,EAAa,KAAK,EAA6B,MAAM,cAAc,CAAC;AAEhF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAoBjD,OAAO,EAAE,KAAK,YAAY,EAAoB,MAAM,gBAAgB,CAAC;AA4CrE,uEAAuE;AACvE,MAAM,MAAM,cAAc,GAAG;IAC3B;;;OAGG;IACH,IAAI,EACA,2BAA2B,GAC3B,gBAAgB,GAChB,0BAA0B,GAC1B,wBAAwB,GACxB,yBAAyB,GACzB,sBAAsB,GACtB,8BAA8B,GAC9B,oBAAoB,GACpB,mBAAmB,GACnB,yBAAyB,GACzB,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAClB,iBAAiB;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,2BAA2B;AAC3B,MAAM,MAAM,cAAc,GAAG;IAC3B,oCAAoC;IACpC,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAC3C;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CAC1C,CAAC;AAsDF;;;GAGG;AACH,eAAO,MAAM,cAAc,GAAI,IAAI,EAAE,EAAE,UAAS,cAAmB,KAAG,KAgOrE,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { rect } from "../geometry/rect.js";
|
|
2
|
+
import { BUILTIN_SHAPES } from "../shapes/index.js";
|
|
2
3
|
import { NameStack } from "./name-stack.js";
|
|
3
4
|
import { applyTransformChain, computeScopeBoundingBox, lowerScopeTransforms, projectLayoutToGlobal, registerScopeAsLayout } from "./scope.js";
|
|
4
5
|
import { resolvePosition } from "./position.js";
|
|
@@ -6,6 +7,7 @@ import { emitNodePrimitives, layoutNode } from "./node.js";
|
|
|
6
7
|
import { fallbackMeasurer } from "./text-metrics.js";
|
|
7
8
|
import { emitPathPrimitive } from "./path/index.js";
|
|
8
9
|
import { makeRound } from "./precision.js";
|
|
10
|
+
import { buildStyleFrame, resolveEffectivePath, resolveLabelDefault, resolveNodeStyle } from "./style.js";
|
|
9
11
|
import { computeLayout } from "./layout.js";
|
|
10
12
|
//#region src/compile/compile.ts
|
|
11
13
|
/**
|
|
@@ -15,7 +17,8 @@ import { computeLayout } from "./layout.js";
|
|
|
15
17
|
*/
|
|
16
18
|
var zeroSizeRectAt = (id, [cx, cy]) => ({
|
|
17
19
|
id,
|
|
18
|
-
|
|
20
|
+
shapeName: "rectangle",
|
|
21
|
+
shapeDef: BUILTIN_SHAPES.rectangle,
|
|
19
22
|
rect: {
|
|
20
23
|
x: cx,
|
|
21
24
|
y: cy,
|
|
@@ -83,6 +86,17 @@ var compileToScene = (ir, options = {}) => {
|
|
|
83
86
|
const round = makeRound(options.precision ?? 2);
|
|
84
87
|
const nodeDistance = options.nodeDistance;
|
|
85
88
|
const onWarn = options.onWarn ?? defaultWarnDispatcher;
|
|
89
|
+
const effectiveShapes = options.shapes ? {
|
|
90
|
+
...BUILTIN_SHAPES,
|
|
91
|
+
...options.shapes
|
|
92
|
+
} : BUILTIN_SHAPES;
|
|
93
|
+
if (options.shapes) {
|
|
94
|
+
for (const name of Object.keys(options.shapes)) if (Object.prototype.hasOwnProperty.call(BUILTIN_SHAPES, name)) onWarn({
|
|
95
|
+
code: "SHAPE_OVERRIDES_BUILTIN",
|
|
96
|
+
message: `Injected shape '${name}' overrides the built-in shape of the same name.`,
|
|
97
|
+
path: `options.shapes.${name}`
|
|
98
|
+
});
|
|
99
|
+
}
|
|
86
100
|
const primitives = [];
|
|
87
101
|
const nameStack = new NameStack({ onDuplicate: (info) => onWarn(formatDuplicateWarning(info)) });
|
|
88
102
|
const allPoints = [];
|
|
@@ -120,12 +134,13 @@ var compileToScene = (ir, options = {}) => {
|
|
|
120
134
|
* @param locatorPrefix IR locator 前缀(如 `''` 表示顶层、`children[2].scope.` 表示某 scope 内)
|
|
121
135
|
* @param layoutsAccumulator 当前 scope 子树所有"实体"layout(node / coordinate / 嵌套 scope.id synthetic)累积——专给上层 scope.id bbox 计算用;顶层调用传一个共享数组(用得着就用,丢弃也不影响)
|
|
122
136
|
* @param pathsAccumulator 当前层级收集的 pending paths——由调用方分配并在合适时机 resolve
|
|
137
|
+
* @param styleStack 从根到当前层级累积的样式 frame 栈(scope 级联 graphic state + 四通道 every-X + resetStyle);node / path 进入时按 inside-out per-field 解析 effective 样式
|
|
123
138
|
*/
|
|
124
|
-
const processChildren = (children, chain, sink, locatorPrefix, layoutsAccumulator, pathsAccumulator) => {
|
|
139
|
+
const processChildren = (children, chain, sink, locatorPrefix, layoutsAccumulator, pathsAccumulator, styleStack) => {
|
|
125
140
|
for (let i = 0; i < children.length; i++) {
|
|
126
141
|
const child = children[i];
|
|
127
142
|
if (child.type === "node") {
|
|
128
|
-
const layout = layoutNode(child, measureText, nameStack, nodeDistance, chain);
|
|
143
|
+
const layout = layoutNode(resolveNodeStyle(child, styleStack), measureText, nameStack, nodeDistance, chain, resolveLabelDefault(styleStack), effectiveShapes);
|
|
129
144
|
const globalLayout = chain.length === 0 ? layout : projectLayoutToGlobal(layout, chain);
|
|
130
145
|
if (child.id) nameStack.register(child.id, globalLayout, `${locatorPrefix}children[${i}].node.id`);
|
|
131
146
|
for (const prim of emitNodePrimitives(layout, round)) sink.push(prim);
|
|
@@ -165,7 +180,7 @@ var compileToScene = (ir, options = {}) => {
|
|
|
165
180
|
* 让 scope 内 path 自引用本 scope.id 端点取真 bbox 而非 placeholder */
|
|
166
181
|
const innerPaths = [];
|
|
167
182
|
try {
|
|
168
|
-
processChildren(child.children, innerChain, innerSink, `${locatorPrefix}children[${i}].scope.`, innerLayouts, innerPaths);
|
|
183
|
+
processChildren(child.children, innerChain, innerSink, `${locatorPrefix}children[${i}].scope.`, innerLayouts, innerPaths, [...styleStack, buildStyleFrame(child)]);
|
|
169
184
|
if (child.id) {
|
|
170
185
|
const bbox = computeScopeBoundingBox(innerLayouts);
|
|
171
186
|
const fallbackOrigin = innerChain.length === 0 ? [0, 0] : applyTransformChain([0, 0], innerChain);
|
|
@@ -186,14 +201,14 @@ var compileToScene = (ir, options = {}) => {
|
|
|
186
201
|
if (hasOwnTransforms) group.transforms = [...ownTransforms];
|
|
187
202
|
sink.push(group);
|
|
188
203
|
} else pathsAccumulator.push({
|
|
189
|
-
path: child,
|
|
204
|
+
path: resolveEffectivePath(child, styleStack),
|
|
190
205
|
irPath: `${locatorPrefix}children[${i}].path`,
|
|
191
206
|
scopeChain: chain
|
|
192
207
|
});
|
|
193
208
|
}
|
|
194
209
|
};
|
|
195
210
|
const rootPaths = [];
|
|
196
|
-
processChildren(ir.children, [], primitives, "", [], rootPaths);
|
|
211
|
+
processChildren(ir.children, [], primitives, "", [], rootPaths, []);
|
|
197
212
|
resolvePendingPaths(rootPaths);
|
|
198
213
|
return {
|
|
199
214
|
primitives,
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { Position } from '../geometry/point';
|
|
2
|
-
import { Rect
|
|
3
|
-
import { AtDirection,
|
|
2
|
+
import { Rect } from '../geometry/rect';
|
|
3
|
+
import { AtDirection, IRLabelDefault, IRNode } from '../ir';
|
|
4
4
|
import { ScenePrimitive, TextLine, Transform } from '../primitive';
|
|
5
|
+
import { ShapeDefinition } from '../shapes';
|
|
5
6
|
import { NameStack } from './name-stack';
|
|
6
7
|
import { TextMeasurer } from './text-metrics';
|
|
7
8
|
export type NodeLayout = {
|
|
8
9
|
/** 节点 id(其他位置可引用) */
|
|
9
10
|
id?: string;
|
|
10
|
-
/**
|
|
11
|
-
|
|
11
|
+
/** 节点形状名(诊断 / 错误信息用;几何走 shapeDef) */
|
|
12
|
+
shapeName: string;
|
|
13
|
+
/** 已解析的 shape 定义;circumscribe / boundaryPoint / anchor / emit 多点复用,取代旧 switch */
|
|
14
|
+
shapeDef: ShapeDefinition;
|
|
12
15
|
/**
|
|
13
16
|
* 节点视觉边界框(所有 shape 共享语义)
|
|
14
17
|
* @description rectangle: rect 本身;circle: width=height=2×radius;ellipse: 2×rx,2×ry;diamond: 2×halfA,2×halfB。x,y 是几何中心,rotate 弧度
|
|
@@ -79,14 +82,15 @@ export type NodeLabelLayout = {
|
|
|
79
82
|
};
|
|
80
83
|
/**
|
|
81
84
|
* 取节点 shape 在 toward 方向的附着点(path 端点贴边用)
|
|
82
|
-
* @description
|
|
85
|
+
* @description 走 shapeDef.boundaryPoint;margin > 0 时先膨胀外接 Rect,让 path 在 border 外停 margin
|
|
83
86
|
*/
|
|
84
87
|
export declare const boundaryPointOf: (layout: NodeLayout, toward: Position) => Position;
|
|
85
88
|
/**
|
|
86
|
-
* 取节点 shape 命名 anchor(center / north / east / north-east
|
|
87
|
-
* @description 不应用 margin——TikZ 语义中 explicit anchor 取视觉边界点不涉及 outer sep;用于 `'A.north'`
|
|
89
|
+
* 取节点 shape 命名 anchor(center / north / east / north-east 等)
|
|
90
|
+
* @description 不应用 margin——TikZ 语义中 explicit anchor 取视觉边界点不涉及 outer sep;用于 `'A.north'` 落点。
|
|
91
|
+
* shapeDef.anchor 不认识的名字返回 undefined,此处抛 Unknown anchor(列出 shape 名)
|
|
88
92
|
*/
|
|
89
|
-
export declare const anchorOf: (layout: NodeLayout, name:
|
|
93
|
+
export declare const anchorOf: (layout: NodeLayout, name: string) => Position;
|
|
90
94
|
/**
|
|
91
95
|
* 取节点 shape 在指定角度方向的边界点
|
|
92
96
|
* @description 角度是节点**局部坐标系**下的极角(度数:0°=局部 +x,90°=局部 +y)。layout.rect.rotate 把局部基绕中心旋转,得到世界系下的视觉方向;shape boundaryPoint 内部用 rotate-aware 投影,所以这里把局部 (cos, sin) 经 rect.rotate 旋转后加到中心当作世界系 toward 传入。不应用 margin(同 anchorOf);用于 `'A.30'` 落点
|
|
@@ -99,10 +103,11 @@ export declare const angleBoundaryOf: (layout: NodeLayout, angleDeg: number) =>
|
|
|
99
103
|
* scope 局部度量),调用方负责后续 `projectLayoutToGlobal` / `applyTransformChain` 投回全局;
|
|
100
104
|
* 笛卡尔字面量 `Position` 已在 scope 局部度量,行为延续 v0.1。
|
|
101
105
|
*/
|
|
102
|
-
export declare const layoutNode: (node: IRNode, measureText: TextMeasurer, nameStack: NameStack, nodeDistance?: number, scopeChain?: ReadonlyArray<Transform>) => NodeLayout;
|
|
106
|
+
export declare const layoutNode: (node: IRNode, measureText: TextMeasurer, nameStack: NameStack, nodeDistance?: number, scopeChain?: ReadonlyArray<Transform>, labelDefault?: IRLabelDefault, shapes?: Record<string, ShapeDefinition>) => NodeLayout;
|
|
103
107
|
/**
|
|
104
108
|
* NodeLayout → Scene primitives
|
|
105
|
-
* @description shape
|
|
109
|
+
* @description shape 主体走 `shapeDef.emit`(收轴对齐 rect、可出多 primitive);text 始终走 TextPrim;
|
|
110
|
+
* 有旋转时外层 GroupPrim 用 `rotate(deg cx cy)` 统一包裹 shape + text(diamond 顶点 / text 都靠 group 旋转)
|
|
106
111
|
*/
|
|
107
112
|
export declare const emitNodePrimitives: (layout: NodeLayout, round: (n: number) => number) => Array<ScenePrimitive>;
|
|
108
113
|
//# sourceMappingURL=node.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../src/compile/node.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../src/compile/node.ts"],"names":[],"mappings":"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"}
|
package/dist/es/compile/node.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { circle } from "../geometry/circle.js";
|
|
3
|
-
import { diamond } from "../geometry/diamond.js";
|
|
4
|
-
import { ellipse } from "../geometry/ellipse.js";
|
|
1
|
+
import { BUILTIN_SHAPES } from "../shapes/index.js";
|
|
5
2
|
import { resolvePosition } from "./position.js";
|
|
6
3
|
//#region src/compile/node.ts
|
|
7
4
|
var DEFAULT_FONT_SIZE = 14;
|
|
@@ -10,7 +7,6 @@ var DEFAULT_LINE_HEIGHT_FACTOR = 1.2;
|
|
|
10
7
|
var DEG_TO_RAD = Math.PI / 180;
|
|
11
8
|
/** Node label 与 node 边界默认距离(TikZ 默认 0pt 视觉太贴) */
|
|
12
9
|
var DEFAULT_LABEL_DISTANCE = 12;
|
|
13
|
-
var SQRT2 = Math.SQRT2;
|
|
14
10
|
/** dashed 预设:4 px 实线 + 2 px 间隙循环 */
|
|
15
11
|
var DASHED_PATTERN = [4, 2];
|
|
16
12
|
/** dotted 预设:1 px 圆点 + 2 px 间隙 */
|
|
@@ -23,61 +19,28 @@ var resolveDashArray = (dashArray, dashed, dotted) => {
|
|
|
23
19
|
};
|
|
24
20
|
/** IR align → 文字对齐锚点(start / middle / end) */
|
|
25
21
|
var alignToTextAnchor = (a) => a === "left" ? "start" : a === "right" ? "end" : "middle";
|
|
26
|
-
/**
|
|
27
|
-
var
|
|
28
|
-
x:
|
|
29
|
-
y:
|
|
30
|
-
width:
|
|
31
|
-
height:
|
|
32
|
-
rotate:
|
|
33
|
-
}
|
|
34
|
-
/** 从 layout 构造 Circle(radius=外接框边长/2 + margin) */
|
|
35
|
-
var circleOf = (layout, marginAdd) => ({
|
|
36
|
-
x: layout.rect.x,
|
|
37
|
-
y: layout.rect.y,
|
|
38
|
-
radius: layout.rect.width / 2 + marginAdd,
|
|
39
|
-
rotate: layout.rect.rotate
|
|
40
|
-
});
|
|
41
|
-
/** 从 layout 构造 Ellipse(rx/ry 各加 margin) */
|
|
42
|
-
var ellipseOf = (layout, marginAdd) => ({
|
|
43
|
-
x: layout.rect.x,
|
|
44
|
-
y: layout.rect.y,
|
|
45
|
-
rx: layout.rect.width / 2 + marginAdd,
|
|
46
|
-
ry: layout.rect.height / 2 + marginAdd,
|
|
47
|
-
rotate: layout.rect.rotate
|
|
48
|
-
});
|
|
49
|
-
/** 从 layout 构造 Diamond(halfA/halfB 各加 margin) */
|
|
50
|
-
var diamondOf = (layout, marginAdd) => ({
|
|
51
|
-
x: layout.rect.x,
|
|
52
|
-
y: layout.rect.y,
|
|
53
|
-
halfA: layout.rect.width / 2 + marginAdd,
|
|
54
|
-
halfB: layout.rect.height / 2 + marginAdd,
|
|
55
|
-
rotate: layout.rect.rotate
|
|
56
|
-
});
|
|
22
|
+
/** 把 Rect 各方向外扩 m(margin generic:所有 shape 都 w+2m, h+2m,由 boundaryPointOf 调用前膨胀) */
|
|
23
|
+
var inflateRect = (r, m) => m === 0 ? r : {
|
|
24
|
+
x: r.x,
|
|
25
|
+
y: r.y,
|
|
26
|
+
width: r.width + 2 * m,
|
|
27
|
+
height: r.height + 2 * m,
|
|
28
|
+
rotate: r.rotate
|
|
29
|
+
};
|
|
57
30
|
/**
|
|
58
31
|
* 取节点 shape 在 toward 方向的附着点(path 端点贴边用)
|
|
59
|
-
* @description
|
|
32
|
+
* @description 走 shapeDef.boundaryPoint;margin > 0 时先膨胀外接 Rect,让 path 在 border 外停 margin
|
|
60
33
|
*/
|
|
61
|
-
var boundaryPointOf = (layout, toward) =>
|
|
62
|
-
const m = layout.margin;
|
|
63
|
-
switch (layout.shape) {
|
|
64
|
-
case "rectangle": return rect.boundaryPoint(rectOf(layout, m), toward);
|
|
65
|
-
case "circle": return circle.boundaryPoint(circleOf(layout, m), toward);
|
|
66
|
-
case "ellipse": return ellipse.boundaryPoint(ellipseOf(layout, m), toward);
|
|
67
|
-
case "diamond": return diamond.boundaryPoint(diamondOf(layout, m), toward);
|
|
68
|
-
}
|
|
69
|
-
};
|
|
34
|
+
var boundaryPointOf = (layout, toward) => layout.shapeDef.boundaryPoint(inflateRect(layout.rect, layout.margin), toward);
|
|
70
35
|
/**
|
|
71
|
-
* 取节点 shape 命名 anchor(center / north / east / north-east
|
|
72
|
-
* @description 不应用 margin——TikZ 语义中 explicit anchor 取视觉边界点不涉及 outer sep;用于 `'A.north'`
|
|
36
|
+
* 取节点 shape 命名 anchor(center / north / east / north-east 等)
|
|
37
|
+
* @description 不应用 margin——TikZ 语义中 explicit anchor 取视觉边界点不涉及 outer sep;用于 `'A.north'` 落点。
|
|
38
|
+
* shapeDef.anchor 不认识的名字返回 undefined,此处抛 Unknown anchor(列出 shape 名)
|
|
73
39
|
*/
|
|
74
40
|
var anchorOf = (layout, name) => {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
case "ellipse": return ellipse.anchor(ellipseOf(layout, 0), name);
|
|
79
|
-
case "diamond": return diamond.anchor(diamondOf(layout, 0), name);
|
|
80
|
-
}
|
|
41
|
+
const p = layout.shapeDef.anchor(layout.rect, name);
|
|
42
|
+
if (p === void 0) throw new Error(`Unknown anchor '${name}' for shape '${layout.shapeName}'`);
|
|
43
|
+
return p;
|
|
81
44
|
};
|
|
82
45
|
/** 8 方向 label position → (anchorName, 单位向量);above 视觉上方即 y 减小 */
|
|
83
46
|
var LABEL_DIRECTION_MAP = {
|
|
@@ -140,12 +103,7 @@ var angleBoundaryOf = (layout, angleDeg) => {
|
|
|
140
103
|
const cosR = Math.cos(rot);
|
|
141
104
|
const sinR = Math.sin(rot);
|
|
142
105
|
const toward = [layout.rect.x + lx * cosR - ly * sinR, layout.rect.y + lx * sinR + ly * cosR];
|
|
143
|
-
|
|
144
|
-
case "rectangle": return rect.boundaryPoint(rectOf(layout, 0), toward);
|
|
145
|
-
case "circle": return circle.boundaryPoint(circleOf(layout, 0), toward);
|
|
146
|
-
case "ellipse": return ellipse.boundaryPoint(ellipseOf(layout, 0), toward);
|
|
147
|
-
case "diamond": return diamond.boundaryPoint(diamondOf(layout, 0), toward);
|
|
148
|
-
}
|
|
106
|
+
return layout.shapeDef.boundaryPoint(layout.rect, toward);
|
|
149
107
|
};
|
|
150
108
|
/**
|
|
151
109
|
* IR Node → 内部 NodeLayout
|
|
@@ -154,7 +112,10 @@ var angleBoundaryOf = (layout, angleDeg) => {
|
|
|
154
112
|
* scope 局部度量),调用方负责后续 `projectLayoutToGlobal` / `applyTransformChain` 投回全局;
|
|
155
113
|
* 笛卡尔字面量 `Position` 已在 scope 局部度量,行为延续 v0.1。
|
|
156
114
|
*/
|
|
157
|
-
var layoutNode = (node, measureText, nameStack, nodeDistance, scopeChain = []) => {
|
|
115
|
+
var layoutNode = (node, measureText, nameStack, nodeDistance, scopeChain = [], labelDefault, shapes = BUILTIN_SHAPES) => {
|
|
116
|
+
const shapeName = node.shape ?? "rectangle";
|
|
117
|
+
const shapeDef = Object.prototype.hasOwnProperty.call(shapes, shapeName) ? shapes[shapeName] : void 0;
|
|
118
|
+
if (!shapeDef) throw new Error(`Unknown shape '${shapeName}'; registered shapes: ${Object.keys(shapes).sort().join(", ")}`);
|
|
158
119
|
const sx = node.xScale ?? node.scale ?? 1;
|
|
159
120
|
const sy = node.yScale ?? node.scale ?? 1;
|
|
160
121
|
const fontScale = Math.min(sx, sy);
|
|
@@ -201,29 +162,7 @@ var layoutNode = (node, measureText, nameStack, nodeDistance, scopeChain = []) =
|
|
|
201
162
|
const minH = node.minimumHeight ?? node.minimumSize ?? 0;
|
|
202
163
|
const innerHalfW = Math.max(textWidth / 2 + xSep, xSep, minW / 2);
|
|
203
164
|
const innerHalfH = Math.max(textHeight / 2 + ySep, ySep, minH / 2);
|
|
204
|
-
const
|
|
205
|
-
let boundsHalfW;
|
|
206
|
-
let boundsHalfH;
|
|
207
|
-
switch (shape) {
|
|
208
|
-
case "rectangle":
|
|
209
|
-
boundsHalfW = innerHalfW;
|
|
210
|
-
boundsHalfH = innerHalfH;
|
|
211
|
-
break;
|
|
212
|
-
case "circle": {
|
|
213
|
-
const r = Math.sqrt(innerHalfW * innerHalfW + innerHalfH * innerHalfH);
|
|
214
|
-
boundsHalfW = r;
|
|
215
|
-
boundsHalfH = r;
|
|
216
|
-
break;
|
|
217
|
-
}
|
|
218
|
-
case "ellipse":
|
|
219
|
-
boundsHalfW = innerHalfW * SQRT2;
|
|
220
|
-
boundsHalfH = innerHalfH * SQRT2;
|
|
221
|
-
break;
|
|
222
|
-
case "diamond":
|
|
223
|
-
boundsHalfW = innerHalfW * 2;
|
|
224
|
-
boundsHalfH = innerHalfH * 2;
|
|
225
|
-
break;
|
|
226
|
-
}
|
|
165
|
+
const { halfWidth: boundsHalfW, halfHeight: boundsHalfH } = shapeDef.circumscribe(innerHalfW, innerHalfH);
|
|
227
166
|
const rotateDeg = node.rotate ?? 0;
|
|
228
167
|
const center = resolvePosition(node.position, nameStack, nodeDistance, scopeChain);
|
|
229
168
|
if (!center) throw new Error(`Cannot resolve position for node ${node.id ?? "(unnamed)"}; polar.origin or at.of may reference an undefined node`);
|
|
@@ -233,17 +172,18 @@ var layoutNode = (node, measureText, nameStack, nodeDistance, scopeChain = []) =
|
|
|
233
172
|
text: lab.text,
|
|
234
173
|
position: lab.position ?? "above",
|
|
235
174
|
distance: lab.distance ?? DEFAULT_LABEL_DISTANCE,
|
|
236
|
-
textColor: lab.textColor ?? node.textColor,
|
|
237
|
-
opacity: lab.opacity,
|
|
238
|
-
fontSize: (labFont?.size ?? baseFontSize) * fontScale,
|
|
239
|
-
fontFamily: labFont?.family ?? fontFamily,
|
|
240
|
-
fontWeight: labFont?.weight ?? fontWeight,
|
|
241
|
-
fontStyle: labFont?.style ?? fontStyle
|
|
175
|
+
textColor: lab.textColor ?? labelDefault?.textColor ?? labelDefault?.color ?? node.textColor,
|
|
176
|
+
opacity: lab.opacity ?? labelDefault?.opacity,
|
|
177
|
+
fontSize: (labFont?.size ?? labelDefault?.font?.size ?? baseFontSize) * fontScale,
|
|
178
|
+
fontFamily: labFont?.family ?? labelDefault?.font?.family ?? fontFamily,
|
|
179
|
+
fontWeight: labFont?.weight ?? labelDefault?.font?.weight ?? fontWeight,
|
|
180
|
+
fontStyle: labFont?.style ?? labelDefault?.font?.style ?? fontStyle
|
|
242
181
|
};
|
|
243
182
|
});
|
|
244
183
|
return {
|
|
245
184
|
id: node.id,
|
|
246
|
-
|
|
185
|
+
shapeName,
|
|
186
|
+
shapeDef,
|
|
247
187
|
rect: {
|
|
248
188
|
x: center[0],
|
|
249
189
|
y: center[1],
|
|
@@ -274,97 +214,28 @@ var layoutNode = (node, measureText, nameStack, nodeDistance, scopeChain = []) =
|
|
|
274
214
|
labels
|
|
275
215
|
};
|
|
276
216
|
};
|
|
277
|
-
/**
|
|
278
|
-
var
|
|
279
|
-
|
|
280
|
-
const halfH = layout.rect.height / 2;
|
|
281
|
-
return {
|
|
282
|
-
type: "rect",
|
|
283
|
-
x: round(layout.rect.x - halfW),
|
|
284
|
-
y: round(layout.rect.y - halfH),
|
|
285
|
-
width: round(layout.rect.width),
|
|
286
|
-
height: round(layout.rect.height),
|
|
287
|
-
fill: layout.fill ?? "transparent",
|
|
288
|
-
fillOpacity: layout.fillOpacity,
|
|
289
|
-
stroke: layout.stroke ?? "currentColor",
|
|
290
|
-
strokeOpacity: layout.strokeOpacity,
|
|
291
|
-
strokeWidth: layout.strokeWidth ?? 1,
|
|
292
|
-
dashPattern: layout.dashPattern,
|
|
293
|
-
cornerRadius: layout.roundedCorners,
|
|
294
|
-
opacity: layout.opacity
|
|
295
|
-
};
|
|
296
|
-
};
|
|
297
|
-
/** circle/ellipse → EllipsePrim(圆形 rx=ry) */
|
|
298
|
-
var emitEllipseShape = (layout, round) => ({
|
|
299
|
-
type: "ellipse",
|
|
300
|
-
cx: round(layout.rect.x),
|
|
301
|
-
cy: round(layout.rect.y),
|
|
302
|
-
rx: round(layout.rect.width / 2),
|
|
303
|
-
ry: round(layout.rect.height / 2),
|
|
304
|
-
fill: layout.fill ?? "transparent",
|
|
217
|
+
/** 从 NodeLayout 收敛 emit 所需的视觉样式子集(ShapeStyle,不含几何 / 文本) */
|
|
218
|
+
var toShapeStyle = (layout) => ({
|
|
219
|
+
fill: layout.fill,
|
|
305
220
|
fillOpacity: layout.fillOpacity,
|
|
306
|
-
stroke: layout.stroke
|
|
221
|
+
stroke: layout.stroke,
|
|
307
222
|
strokeOpacity: layout.strokeOpacity,
|
|
308
|
-
strokeWidth: layout.strokeWidth
|
|
223
|
+
strokeWidth: layout.strokeWidth,
|
|
309
224
|
dashPattern: layout.dashPattern,
|
|
225
|
+
roundedCorners: layout.roundedCorners,
|
|
310
226
|
opacity: layout.opacity
|
|
311
227
|
});
|
|
312
|
-
/** diamond → PathPrim(4 顶点 + close 闭合) */
|
|
313
|
-
var emitDiamondShape = (layout, round) => {
|
|
314
|
-
const diam = diamondOf(layout, 0);
|
|
315
|
-
const e = diamond.anchor(diam, "east");
|
|
316
|
-
const n = diamond.anchor(diam, "north");
|
|
317
|
-
const w = diamond.anchor(diam, "west");
|
|
318
|
-
const s = diamond.anchor(diam, "south");
|
|
319
|
-
return {
|
|
320
|
-
type: "path",
|
|
321
|
-
commands: [
|
|
322
|
-
{
|
|
323
|
-
kind: "move",
|
|
324
|
-
to: [round(e[0]), round(e[1])]
|
|
325
|
-
},
|
|
326
|
-
{
|
|
327
|
-
kind: "line",
|
|
328
|
-
to: [round(n[0]), round(n[1])]
|
|
329
|
-
},
|
|
330
|
-
{
|
|
331
|
-
kind: "line",
|
|
332
|
-
to: [round(w[0]), round(w[1])]
|
|
333
|
-
},
|
|
334
|
-
{
|
|
335
|
-
kind: "line",
|
|
336
|
-
to: [round(s[0]), round(s[1])]
|
|
337
|
-
},
|
|
338
|
-
{ kind: "close" }
|
|
339
|
-
],
|
|
340
|
-
fill: layout.fill ?? "transparent",
|
|
341
|
-
fillOpacity: layout.fillOpacity,
|
|
342
|
-
stroke: layout.stroke ?? "currentColor",
|
|
343
|
-
strokeOpacity: layout.strokeOpacity,
|
|
344
|
-
strokeWidth: layout.strokeWidth ?? 1,
|
|
345
|
-
dashPattern: layout.dashPattern,
|
|
346
|
-
opacity: layout.opacity
|
|
347
|
-
};
|
|
348
|
-
};
|
|
349
228
|
/**
|
|
350
229
|
* NodeLayout → Scene primitives
|
|
351
|
-
* @description shape
|
|
230
|
+
* @description shape 主体走 `shapeDef.emit`(收轴对齐 rect、可出多 primitive);text 始终走 TextPrim;
|
|
231
|
+
* 有旋转时外层 GroupPrim 用 `rotate(deg cx cy)` 统一包裹 shape + text(diamond 顶点 / text 都靠 group 旋转)
|
|
352
232
|
*/
|
|
353
233
|
var emitNodePrimitives = (layout, round) => {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
case "circle":
|
|
360
|
-
case "ellipse":
|
|
361
|
-
shapePrim = emitEllipseShape(layout, round);
|
|
362
|
-
break;
|
|
363
|
-
case "diamond":
|
|
364
|
-
shapePrim = emitDiamondShape(unrotated(layout), round);
|
|
365
|
-
break;
|
|
366
|
-
}
|
|
367
|
-
const inner = [shapePrim];
|
|
234
|
+
const axisAlignedRect = {
|
|
235
|
+
...layout.rect,
|
|
236
|
+
rotate: 0
|
|
237
|
+
};
|
|
238
|
+
const inner = [...layout.shapeDef.emit(axisAlignedRect, toShapeStyle(layout), round)];
|
|
368
239
|
if (layout.lines) {
|
|
369
240
|
const halfBlockW = layout.textWidth / 2;
|
|
370
241
|
const xOffset = layout.align === "start" ? -halfBlockW : layout.align === "end" ? halfBlockW : 0;
|
|
@@ -418,13 +289,5 @@ var emitNodePrimitives = (layout, round) => {
|
|
|
418
289
|
children: inner
|
|
419
290
|
}];
|
|
420
291
|
};
|
|
421
|
-
/** layout 的"未旋转"副本,让 diamond 顶点先按未旋转算,外层 group 统一旋转 */
|
|
422
|
-
var unrotated = (layout) => ({
|
|
423
|
-
...layout,
|
|
424
|
-
rect: {
|
|
425
|
-
...layout.rect,
|
|
426
|
-
rotate: 0
|
|
427
|
-
}
|
|
428
|
-
});
|
|
429
292
|
//#endregion
|
|
430
293
|
export { anchorOf, angleBoundaryOf, boundaryPointOf, emitNodePrimitives, layoutNode };
|
|
@@ -47,7 +47,7 @@ var emitPathPrimitive = (path, nameStack, round, measureText = fallbackMeasurer,
|
|
|
47
47
|
const collectLabel = (step, sampleAt) => {
|
|
48
48
|
if (step.kind === "move" || step.kind === "cycle" || !("label" in step) || !step.label) return;
|
|
49
49
|
const sample = sampleAt(tForLabelPosition(step.label.position));
|
|
50
|
-
const r = emitLabelPrimitive(step.label, sample, measureText, round);
|
|
50
|
+
const r = emitLabelPrimitive(step.label, sample, measureText, round, path.opacity);
|
|
51
51
|
labelPrims.push(r.primitive);
|
|
52
52
|
for (const p of r.points) points.push(p);
|
|
53
53
|
};
|
|
@@ -11,7 +11,7 @@ export declare const tForLabelPosition: (pos: IRStepLabel["position"]) => number
|
|
|
11
11
|
* step.label + 段采样 → TextPrim(sloped 时裹一层 group 旋转)
|
|
12
12
|
* @description 默认 side='above'/position='midway':above/below 锚点 y±offset、align=middle、baseline=bottom/top;left/right x±offset、align=end/start、baseline=middle;sloped 不偏移裹 group rotate(angle, cx, cy) 由切线 atan2 算(SVG y-down CW 正)。返回 primitive + layout 外接点
|
|
13
13
|
*/
|
|
14
|
-
export declare const emitLabelPrimitive: (label: IRStepLabel, sample: SegmentSample, measureText: TextMeasurer, round: (n: number) => number) => {
|
|
14
|
+
export declare const emitLabelPrimitive: (label: IRStepLabel, sample: SegmentSample, measureText: TextMeasurer, round: (n: number) => number, hostOpacity?: number) => {
|
|
15
15
|
primitive: ScenePrimitive;
|
|
16
16
|
points: Array<IRPosition>;
|
|
17
17
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"label.d.ts","sourceRoot":"","sources":["../../../../src/compile/path/label.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAY,MAAM,iBAAiB,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAmBpD;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,KAAK,WAAW,CAAC,UAAU,CAAC,KAAG,MAIhE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAC7B,OAAO,WAAW,EAClB,QAAQ,aAAa,EACrB,aAAa,YAAY,EACzB,OAAO,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,
|
|
1
|
+
{"version":3,"file":"label.d.ts","sourceRoot":"","sources":["../../../../src/compile/path/label.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAY,MAAM,iBAAiB,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAmBpD;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,KAAK,WAAW,CAAC,UAAU,CAAC,KAAG,MAIhE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAC7B,OAAO,WAAW,EAClB,QAAQ,aAAa,EACrB,aAAa,YAAY,EACzB,OAAO,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAC5B,cAAc,MAAM,KACnB;IAAE,SAAS,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAA;CAkGxD,CAAC"}
|
|
@@ -27,13 +27,22 @@ var tForLabelPosition = (pos) => {
|
|
|
27
27
|
* step.label + 段采样 → TextPrim(sloped 时裹一层 group 旋转)
|
|
28
28
|
* @description 默认 side='above'/position='midway':above/below 锚点 y±offset、align=middle、baseline=bottom/top;left/right x±offset、align=end/start、baseline=middle;sloped 不偏移裹 group rotate(angle, cx, cy) 由切线 atan2 算(SVG y-down CW 正)。返回 primitive + layout 外接点
|
|
29
29
|
*/
|
|
30
|
-
var emitLabelPrimitive = (label, sample, measureText, round) => {
|
|
31
|
-
const fontSize = LABEL_FONT_SIZE;
|
|
30
|
+
var emitLabelPrimitive = (label, sample, measureText, round, hostOpacity) => {
|
|
31
|
+
const fontSize = label.font?.size ?? LABEL_FONT_SIZE;
|
|
32
|
+
const fontFamily = label.font?.family;
|
|
33
|
+
const fontWeight = label.font?.weight;
|
|
34
|
+
const fontStyle = label.font?.style;
|
|
32
35
|
const lineHeight = fontSize * LABEL_LINE_HEIGHT_FACTOR;
|
|
33
|
-
const m = measureText(label.text, {
|
|
36
|
+
const m = measureText(label.text, {
|
|
37
|
+
size: fontSize,
|
|
38
|
+
family: fontFamily,
|
|
39
|
+
weight: fontWeight,
|
|
40
|
+
style: fontStyle
|
|
41
|
+
});
|
|
34
42
|
const measuredWidth = m.width;
|
|
35
43
|
const measuredHeight = m.height || lineHeight;
|
|
36
44
|
const side = label.side ?? "above";
|
|
45
|
+
const labelOpacity = label.opacity !== void 0 ? hostOpacity !== void 0 ? label.opacity * hostOpacity : label.opacity : hostOpacity;
|
|
37
46
|
let x = sample.point[0];
|
|
38
47
|
let y = sample.point[1];
|
|
39
48
|
let align = "middle";
|
|
@@ -62,8 +71,12 @@ var emitLabelPrimitive = (label, sample, measureText, round) => {
|
|
|
62
71
|
lineHeight: round(lineHeight),
|
|
63
72
|
measuredWidth: round(measuredWidth),
|
|
64
73
|
measuredHeight: round(measuredHeight),
|
|
65
|
-
fill: "currentColor"
|
|
74
|
+
fill: label.textColor ?? "currentColor"
|
|
66
75
|
};
|
|
76
|
+
if (fontFamily !== void 0) text.fontFamily = fontFamily;
|
|
77
|
+
if (fontWeight !== void 0) text.fontWeight = fontWeight;
|
|
78
|
+
if (fontStyle !== void 0) text.fontStyle = fontStyle;
|
|
79
|
+
if (labelOpacity !== void 0) text.opacity = labelOpacity;
|
|
67
80
|
if (side === "sloped") {
|
|
68
81
|
const groupPrim = {
|
|
69
82
|
type: "group",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../../src/compile/scope.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,UAAU,EACV,WAAW,EAEZ,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../../../src/compile/scope.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,UAAU,EACV,WAAW,EAEZ,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAGzC;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAC/B,YAAY,aAAa,CAAC,WAAW,CAAC,EACtC,WAAW,SAAS,EACpB,eAAe,MAAM,KACpB,KAAK,CAAC,SAAS,CAAC,GAAG,IAiDrB,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAC9B,OAAO,UAAU,EACjB,OAAO,aAAa,CAAC,SAAS,CAAC,KAC9B,UAyBF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,GAChC,QAAQ,UAAU,EAClB,OAAO,aAAa,CAAC,SAAS,CAAC,KAC9B,UAgCF,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,GAChC,QAAQ,UAAU,EAClB,OAAO,aAAa,CAAC,SAAS,CAAC,KAC9B,UAuBF,CAAC;AAEF,8EAA8E;AAC9E,MAAM,MAAM,gBAAgB,GAAG;IAC7B,wBAAwB;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,wBAAwB;IACxB,CAAC,EAAE,MAAM,CAAC;IACV,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,GAClC,SAAS,aAAa,CAAC,UAAU,CAAC,KACjC,gBAAgB,GAAG,IA2BrB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GAChC,IAAI,MAAM,EACV,MAAM,gBAAgB,GAAG,IAAI,EAC7B,gBAAgB,UAAU,KACzB,UAgBF,CAAC"}
|
package/dist/es/compile/scope.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { rect } from "../geometry/rect.js";
|
|
2
|
+
import { BUILTIN_SHAPES } from "../shapes/index.js";
|
|
2
3
|
import { resolvePosition } from "./position.js";
|
|
3
4
|
//#region src/compile/scope.ts
|
|
4
5
|
/**
|
|
@@ -235,7 +236,8 @@ var registerScopeAsLayout = (id, bbox, fallbackOrigin) => {
|
|
|
235
236
|
};
|
|
236
237
|
return {
|
|
237
238
|
id,
|
|
238
|
-
|
|
239
|
+
shapeName: "rectangle",
|
|
240
|
+
shapeDef: BUILTIN_SHAPES.rectangle,
|
|
239
241
|
rect: {
|
|
240
242
|
x: box.x,
|
|
241
243
|
y: box.y,
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { IRArrowDetail, IRLabelDefault, IRNode, IRPath, IRScope } from '../ir';
|
|
2
|
+
/**
|
|
3
|
+
* scope 级联 graphic state——主色 color + 跨类共享分项
|
|
4
|
+
* @description 级联到 scope 内全部元素(= TikZ scope option / current color)
|
|
5
|
+
*/
|
|
6
|
+
type CascadeState = Pick<IRScope, 'color' | 'stroke' | 'fill' | 'strokeWidth' | 'opacity' | 'fillOpacity' | 'drawOpacity'>;
|
|
7
|
+
/**
|
|
8
|
+
* 单层 scope 的样式 frame——compile 维护 frame 栈做 inside-out per-field 解析
|
|
9
|
+
* @description 级联 graphic state + 四通道 every-X 默认 + resetStyle 屏障;从 IRScope 抽取(buildStyleFrame)
|
|
10
|
+
*/
|
|
11
|
+
export type StyleFrame = {
|
|
12
|
+
/** 级联 graphic state(主色 + 跨类共享分项) */
|
|
13
|
+
cascade: CascadeState;
|
|
14
|
+
/** every node 默认 */
|
|
15
|
+
nodeDefault?: IRScope['nodeDefault'];
|
|
16
|
+
/** every path 默认 */
|
|
17
|
+
pathDefault?: IRScope['pathDefault'];
|
|
18
|
+
/** every label 默认(node label + step label 共享) */
|
|
19
|
+
labelDefault?: IRLabelDefault;
|
|
20
|
+
/** every arrow 默认 */
|
|
21
|
+
arrowDefault?: IRArrowDetail;
|
|
22
|
+
/** 朝外的继承屏障:切外层对应通道 */
|
|
23
|
+
resetStyle?: IRScope['resetStyle'];
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* 从 IRScope 抽取样式 frame
|
|
27
|
+
* @description 只摘样式相关字段(级联 graphic state + 四通道 + resetStyle);transforms / id / localNamespace 与样式正交,不进 frame
|
|
28
|
+
*/
|
|
29
|
+
export declare const buildStyleFrame: (scope: IRScope) => StyleFrame;
|
|
30
|
+
/**
|
|
31
|
+
* 解析 node 最终样式——fold 外→内 frame 栈 + 元素显式
|
|
32
|
+
* @description 优先级链(每分项就近 model A):元素显式分项 > 元素 color > nodeDefault 分项 > nodeDefault color
|
|
33
|
+
* > scope 级联分项 > scope color > 内置(layoutNode 兜底)。同 frame 内 nodeDefault 优先于级联。
|
|
34
|
+
* resetStyle('node') 丢外层累积;position / id / text / label 取元素自身(不参与继承)。
|
|
35
|
+
*/
|
|
36
|
+
export declare const resolveNodeStyle: (node: IRNode, stack: ReadonlyArray<StyleFrame>) => IRNode;
|
|
37
|
+
/** fold labelDefault 通道(node label + step label 共享);resetStyle('label') 丢外层 */
|
|
38
|
+
export declare const resolveLabelDefault: (stack: ReadonlyArray<StyleFrame>) => IRLabelDefault;
|
|
39
|
+
/**
|
|
40
|
+
* 解析 path 最终样式——fold frame 栈 + 元素显式 + arrow / step-label 跟宿主主色
|
|
41
|
+
* @description 返回 effective IRPath:base 样式 fold(优先级链同 node);arrowDetail 消费 arrowDefault 通道 + 跟主色;
|
|
42
|
+
* 每个 step.label 消费 labelDefault 通道 + 跟主色。masterColor = path 已解析主色(就近 color),arrow / step-label 跟它(不跟 stroke)。
|
|
43
|
+
*/
|
|
44
|
+
export declare const resolveEffectivePath: (path: IRPath, stack: ReadonlyArray<StyleFrame>) => IRPath;
|
|
45
|
+
export {};
|
|
46
|
+
//# sourceMappingURL=style.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../../../src/compile/style.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EAGb,cAAc,EACd,MAAM,EACN,MAAM,EACN,OAAO,EAIR,MAAM,OAAO,CAAC;AAEf;;;GAGG;AACH,KAAK,YAAY,GAAG,IAAI,CACtB,OAAO,EACP,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG,aAAa,GAAG,SAAS,GAAG,aAAa,GAAG,aAAa,CACxF,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,oCAAoC;IACpC,OAAO,EAAE,YAAY,CAAC;IACtB,oBAAoB;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IACrC,oBAAoB;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IACrC,iDAAiD;IACjD,YAAY,CAAC,EAAE,cAAc,CAAC;IAC9B,qBAAqB;IACrB,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,sBAAsB;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;CACpC,CAAC;AAYF;;;GAGG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,OAAO,KAAG,UAgBhD,CAAC;AAiEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAC3B,MAAM,MAAM,EACZ,OAAO,aAAa,CAAC,UAAU,CAAC,KAC/B,MAYF,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,mBAAmB,GAC9B,OAAO,aAAa,CAAC,UAAU,CAAC,KAC/B,cAOF,CAAC;AAyHF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAC/B,MAAM,MAAM,EACZ,OAAO,aAAa,CAAC,UAAU,CAAC,KAC/B,MA2BF,CAAC"}
|