@yalehwang/plantuml-js 0.2.2 → 0.3.1
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/README.md +20 -0
- package/dist/core.d.ts +106 -13
- package/dist/core.js +92 -52
- package/dist/foundation/labels.d.ts +44 -0
- package/dist/foundation/labels.js +231 -0
- package/dist/foundation/layers.d.ts +25 -0
- package/dist/foundation/layers.js +101 -0
- package/dist/foundation/layout.d.ts +116 -0
- package/dist/foundation/layout.js +298 -0
- package/dist/{model.d.ts → foundation/model.d.ts} +20 -0
- package/dist/foundation/relation-semantics.d.ts +49 -0
- package/dist/foundation/relation-semantics.js +112 -0
- package/dist/{text-measure.d.ts → foundation/text-measure.d.ts} +2 -0
- package/dist/{text-measure.js → foundation/text-measure.js} +20 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/layout/compound.d.ts +74 -0
- package/dist/layout/compound.js +1204 -0
- package/dist/layout/elk-graph.d.ts +36 -0
- package/dist/layout/elk-graph.js +220 -0
- package/dist/layout/elk-result.d.ts +18 -0
- package/dist/layout/elk-result.js +127 -0
- package/dist/layout/elk.d.ts +80 -33
- package/dist/layout/elk.js +229 -320
- package/dist/layout/fanout-trunk.d.ts +18 -0
- package/dist/layout/fanout-trunk.js +380 -0
- package/dist/layout/grid-router.d.ts +69 -0
- package/dist/layout/grid-router.js +615 -0
- package/dist/layout/lane-spread.d.ts +11 -0
- package/dist/layout/lane-spread.js +302 -0
- package/dist/layout/package-endpoints.d.ts +10 -0
- package/dist/layout/package-endpoints.js +54 -0
- package/dist/layout/placement-strategy.d.ts +98 -0
- package/dist/layout/placement-strategy.js +26 -0
- package/dist/layout/strategy.d.ts +27 -0
- package/dist/layout/strategy.js +19 -0
- package/dist/layout/trunk-lanes.d.ts +29 -0
- package/dist/layout/trunk-lanes.js +308 -0
- package/dist/layout/viz.d.ts +3 -3
- package/dist/layout/viz.js +13 -17
- package/dist/metrics/layout-metrics.d.ts +183 -0
- package/dist/metrics/layout-metrics.js +766 -0
- package/dist/parse/parser.d.ts +43 -0
- package/dist/{parser.js → parse/parser.js} +111 -5
- package/dist/parse/serialize.d.ts +32 -0
- package/dist/parse/serialize.js +218 -0
- package/dist/render/legend.d.ts +15 -0
- package/dist/render/legend.js +42 -0
- package/dist/render/svg.d.ts +60 -3
- package/dist/render/svg.js +326 -98
- package/dist/render/theme.d.ts +109 -0
- package/dist/render/theme.js +99 -0
- package/dist/{transform.d.ts → transform/transform.d.ts} +55 -1
- package/dist/{transform.js → transform/transform.js} +277 -29
- package/package.json +4 -3
- package/dist/layout-metrics.d.ts +0 -42
- package/dist/layout-metrics.js +0 -241
- package/dist/layout.d.ts +0 -75
- package/dist/layout.js +0 -51
- package/dist/parser.d.ts +0 -17
- /package/dist/{edge-router.d.ts → foundation/edge-router.d.ts} +0 -0
- /package/dist/{edge-router.js → foundation/edge-router.js} +0 -0
- /package/dist/{model.js → foundation/model.js} +0 -0
- /package/dist/{render-dot.d.ts → layout/render-dot.d.ts} +0 -0
- /package/dist/{render-dot.js → layout/render-dot.js} +0 -0
package/dist/layout-metrics.js
DELETED
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Layout quality metrics. Pure functions from (model, layout) to numbers, so a
|
|
3
|
-
* layout strategy change can be judged by a diff in figures rather than by eye.
|
|
4
|
-
*
|
|
5
|
-
* Edge polylines are taken at face value: for the viz engine they are bezier
|
|
6
|
-
* control points, which is a coarse but consistent approximation. Every metric
|
|
7
|
-
* is deterministic — the same (model, layout) always yields the same numbers —
|
|
8
|
-
* so a run can be committed as `rendered/metrics.baseline.json` and diffed
|
|
9
|
-
* against a later run to prove an improvement or a regression.
|
|
10
|
-
*/
|
|
11
|
-
import { METRICS, displayName } from './layout.js';
|
|
12
|
-
import { countEdgeBoxIntersections, nodeRect } from './edge-router.js';
|
|
13
|
-
import { measureTextWidth } from './text-measure.js';
|
|
14
|
-
/** Package label tab geometry, kept in step with [[../render/svg]]. */
|
|
15
|
-
const PACKAGE_TAB_HEIGHT = 18;
|
|
16
|
-
const PACKAGE_LABEL_FONT_SIZE = 12;
|
|
17
|
-
const PACKAGE_TAB_PADDING = 12;
|
|
18
|
-
export function computeLayoutMetrics(model, layout) {
|
|
19
|
-
const edges = layout.edges ?? [];
|
|
20
|
-
const nodeArea = layout.nodes.reduce((sum, n) => sum + n.width * n.height, 0);
|
|
21
|
-
const canvasArea = layout.width * layout.height;
|
|
22
|
-
return {
|
|
23
|
-
edgeCrossings: countEdgeCrossings(edges.map((e) => e.points)),
|
|
24
|
-
edgeNodeIntersections: countEdgeBoxIntersections(edges, layout.nodes),
|
|
25
|
-
totalEdgeLength: edges.reduce((sum, e) => sum + polylineLength(e.points), 0),
|
|
26
|
-
areaUtilization: canvasArea > 0 ? nodeArea / canvasArea : 0,
|
|
27
|
-
aspectRatio: layout.height > 0 ? layout.width / layout.height : 0,
|
|
28
|
-
textOverflowCount: countTextOverflow(model, layout),
|
|
29
|
-
labelOverlapCount: countLabelOverlap(model, layout),
|
|
30
|
-
overlappingEdgeLength: countOverlappingEdgeLength(edges.map((e) => e.points)),
|
|
31
|
-
hubArrowStack: countHubArrowStack(edges),
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
function polylineLength(points) {
|
|
35
|
-
let len = 0;
|
|
36
|
-
for (let i = 0; i + 1 < points.length; i++) {
|
|
37
|
-
len += Math.hypot(points[i + 1].x - points[i].x, points[i + 1].y - points[i].y);
|
|
38
|
-
}
|
|
39
|
-
return len;
|
|
40
|
-
}
|
|
41
|
-
function orient(a, b, c) {
|
|
42
|
-
const v = (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
|
|
43
|
-
return Math.abs(v) < 1e-9 ? 0 : v > 0 ? 1 : -1;
|
|
44
|
-
}
|
|
45
|
-
/** True when segments ab and cd cross at a point interior to both. */
|
|
46
|
-
function segmentsCross(a, b, c, d) {
|
|
47
|
-
const o1 = orient(a, b, c);
|
|
48
|
-
const o2 = orient(a, b, d);
|
|
49
|
-
const o3 = orient(c, d, a);
|
|
50
|
-
const o4 = orient(c, d, b);
|
|
51
|
-
return o1 * o2 < 0 && o3 * o4 < 0;
|
|
52
|
-
}
|
|
53
|
-
function countEdgeCrossings(polylines) {
|
|
54
|
-
const segs = [];
|
|
55
|
-
polylines.forEach((pts, edge) => {
|
|
56
|
-
for (let i = 0; i + 1 < pts.length; i++) {
|
|
57
|
-
const a = pts[i];
|
|
58
|
-
const b = pts[i + 1];
|
|
59
|
-
segs.push({
|
|
60
|
-
a,
|
|
61
|
-
b,
|
|
62
|
-
edge,
|
|
63
|
-
minX: Math.min(a.x, b.x),
|
|
64
|
-
maxX: Math.max(a.x, b.x),
|
|
65
|
-
minY: Math.min(a.y, b.y),
|
|
66
|
-
maxY: Math.max(a.y, b.y),
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
segs.sort((p, q) => p.minX - q.minX);
|
|
71
|
-
let count = 0;
|
|
72
|
-
for (let i = 0; i < segs.length; i++) {
|
|
73
|
-
const s = segs[i];
|
|
74
|
-
for (let j = i + 1; j < segs.length; j++) {
|
|
75
|
-
const t = segs[j];
|
|
76
|
-
if (t.minX > s.maxX)
|
|
77
|
-
break;
|
|
78
|
-
if (t.edge === s.edge)
|
|
79
|
-
continue;
|
|
80
|
-
if (t.maxY < s.minY || t.minY > s.maxY)
|
|
81
|
-
continue;
|
|
82
|
-
if (segmentsCross(s.a, s.b, t.a, t.b))
|
|
83
|
-
count++;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return count;
|
|
87
|
-
}
|
|
88
|
-
function countTextOverflow(model, layout) {
|
|
89
|
-
const nodes = new Map(layout.nodes.map((n) => [n.id, n]));
|
|
90
|
-
let count = 0;
|
|
91
|
-
for (const entity of model.entities) {
|
|
92
|
-
const node = nodes.get(entity.id);
|
|
93
|
-
if (!node)
|
|
94
|
-
continue;
|
|
95
|
-
const lines = [displayName(entity), ...entity.members.map((m) => m.raw)];
|
|
96
|
-
for (const line of lines) {
|
|
97
|
-
const needed = measureTextWidth(line, METRICS.fontSize) + METRICS.paddingX * 2;
|
|
98
|
-
if (needed > node.width + 0.01)
|
|
99
|
-
count++;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
return count;
|
|
103
|
-
}
|
|
104
|
-
function rectsOverlap(a, b) {
|
|
105
|
-
return (a.x < b.x + b.width &&
|
|
106
|
-
b.x < a.x + a.width &&
|
|
107
|
-
a.y < b.y + b.height &&
|
|
108
|
-
b.y < a.y + a.height);
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* A package's label tab is drawn at its top-left corner. It is counted when it
|
|
112
|
-
* overlaps an entity or sub-package frame inside the package, or when the tab
|
|
113
|
-
* is narrower than the label text it has to hold.
|
|
114
|
-
*/
|
|
115
|
-
function countLabelOverlap(model, layout) {
|
|
116
|
-
const frames = new Map((layout.packages ?? []).map((p) => [p.id, p]));
|
|
117
|
-
const nodes = new Map(layout.nodes.map((n) => [n.id, n]));
|
|
118
|
-
let count = 0;
|
|
119
|
-
for (const pkg of model.packages) {
|
|
120
|
-
const frame = frames.get(pkg.id);
|
|
121
|
-
if (!frame)
|
|
122
|
-
continue;
|
|
123
|
-
const textWidth = measureTextWidth(pkg.name, PACKAGE_LABEL_FONT_SIZE);
|
|
124
|
-
const wanted = textWidth + PACKAGE_TAB_PADDING;
|
|
125
|
-
const tab = {
|
|
126
|
-
x: frame.x,
|
|
127
|
-
y: frame.y,
|
|
128
|
-
width: Math.min(frame.width, Math.max(24, wanted)),
|
|
129
|
-
height: PACKAGE_TAB_HEIGHT,
|
|
130
|
-
};
|
|
131
|
-
let bad = textWidth + 6 > tab.width + 0.01;
|
|
132
|
-
for (const entity of model.entities) {
|
|
133
|
-
if (bad)
|
|
134
|
-
break;
|
|
135
|
-
if (entity.packageId !== pkg.id)
|
|
136
|
-
continue;
|
|
137
|
-
const node = nodes.get(entity.id);
|
|
138
|
-
if (node && rectsOverlap(tab, nodeRect(node)))
|
|
139
|
-
bad = true;
|
|
140
|
-
}
|
|
141
|
-
for (const child of model.packages) {
|
|
142
|
-
if (bad)
|
|
143
|
-
break;
|
|
144
|
-
if (child.parentId !== pkg.id)
|
|
145
|
-
continue;
|
|
146
|
-
const childFrame = frames.get(child.id);
|
|
147
|
-
if (childFrame && rectsOverlap(tab, childFrame))
|
|
148
|
-
bad = true;
|
|
149
|
-
}
|
|
150
|
-
if (bad)
|
|
151
|
-
count++;
|
|
152
|
-
}
|
|
153
|
-
return count;
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Summed length of collinear, overlapping segments from different edges. Two
|
|
157
|
-
* axis-aligned segments that lie on the same line and overlap are a
|
|
158
|
-
* "parallel-line wall": they never produce a proper crossing (so
|
|
159
|
-
* [[countEdgeCrossings]] misses them) yet still read as clutter. Each
|
|
160
|
-
* (segment, segment) overlap is counted once, in canvas pixels.
|
|
161
|
-
*/
|
|
162
|
-
function countOverlappingEdgeLength(polylines) {
|
|
163
|
-
const segs = [];
|
|
164
|
-
polylines.forEach((pts, edge) => {
|
|
165
|
-
for (let i = 0; i + 1 < pts.length; i++) {
|
|
166
|
-
segs.push({ a: pts[i], b: pts[i + 1], edge });
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
let total = 0;
|
|
170
|
-
for (let i = 0; i < segs.length; i++) {
|
|
171
|
-
for (let j = i + 1; j < segs.length; j++) {
|
|
172
|
-
if (segs[i].edge === segs[j].edge)
|
|
173
|
-
continue;
|
|
174
|
-
total += collinearOverlapLength(segs[i].a, segs[i].b, segs[j].a, segs[j].b);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return total;
|
|
178
|
-
}
|
|
179
|
-
/** Overlap length of two segments when collinear, else 0 (in canvas pixels). */
|
|
180
|
-
function collinearOverlapLength(a, b, c, d) {
|
|
181
|
-
const ux = b.x - a.x;
|
|
182
|
-
const uy = b.y - a.y;
|
|
183
|
-
const vx = d.x - c.x;
|
|
184
|
-
const vy = d.y - c.y;
|
|
185
|
-
const lenU = Math.hypot(ux, uy);
|
|
186
|
-
const lenV = Math.hypot(vx, vy);
|
|
187
|
-
if (lenU < 1e-9 || lenV < 1e-9)
|
|
188
|
-
return 0;
|
|
189
|
-
// Parallel: the two direction vectors must be colinear.
|
|
190
|
-
if (Math.abs(ux * vy - uy * vx) > 1e-9 * lenU * lenV)
|
|
191
|
-
return 0;
|
|
192
|
-
// Colinear: `c` must lie on the line through `a`/`b` (perpendicular distance).
|
|
193
|
-
const distC = Math.abs(ux * (c.y - a.y) - uy * (c.x - a.x)) / lenU;
|
|
194
|
-
if (distC > 0.01)
|
|
195
|
-
return 0;
|
|
196
|
-
const uxN = ux / lenU;
|
|
197
|
-
const uyN = uy / lenU;
|
|
198
|
-
const proj = (p) => (p.x - a.x) * uxN + (p.y - a.y) * uyN;
|
|
199
|
-
const t0 = Math.min(proj(a), proj(b));
|
|
200
|
-
const t1 = Math.max(proj(a), proj(b));
|
|
201
|
-
const s0 = Math.min(proj(c), proj(d));
|
|
202
|
-
const s1 = Math.max(proj(c), proj(d));
|
|
203
|
-
const overlap = Math.min(t1, s1) - Math.max(t0, s0);
|
|
204
|
-
return overlap > 0 ? overlap : 0;
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* Incoming arrowheads are grouped by (target node, entry side). A side that
|
|
208
|
-
* receives more than [[HUB_ARROW_STACK_THRESHOLD]] arrowheads contributes its
|
|
209
|
-
* excess to the total — the "everyone points at this hub from the same side"
|
|
210
|
-
* pile-up, invisible to [[countEdgeCrossings]].
|
|
211
|
-
*/
|
|
212
|
-
const HUB_ARROW_STACK_THRESHOLD = 3;
|
|
213
|
-
function countHubArrowStack(edges) {
|
|
214
|
-
const groups = new Map();
|
|
215
|
-
for (const edge of edges) {
|
|
216
|
-
const pts = edge.points;
|
|
217
|
-
if (pts.length < 2)
|
|
218
|
-
continue;
|
|
219
|
-
const side = approachSide(pts[pts.length - 2], pts[pts.length - 1]);
|
|
220
|
-
if (side === undefined)
|
|
221
|
-
continue;
|
|
222
|
-
const key = `${edge.target}\u0000${side}`;
|
|
223
|
-
groups.set(key, (groups.get(key) ?? 0) + 1);
|
|
224
|
-
}
|
|
225
|
-
let total = 0;
|
|
226
|
-
for (const count of groups.values()) {
|
|
227
|
-
if (count > HUB_ARROW_STACK_THRESHOLD)
|
|
228
|
-
total += count - HUB_ARROW_STACK_THRESHOLD;
|
|
229
|
-
}
|
|
230
|
-
return total;
|
|
231
|
-
}
|
|
232
|
-
/** Side of the target the arrowhead enters from, derived from the last segment. */
|
|
233
|
-
function approachSide(from, to) {
|
|
234
|
-
const dx = to.x - from.x;
|
|
235
|
-
const dy = to.y - from.y;
|
|
236
|
-
if (Math.abs(dx) < 1e-9 && Math.abs(dy) < 1e-9)
|
|
237
|
-
return undefined;
|
|
238
|
-
if (Math.abs(dx) >= Math.abs(dy))
|
|
239
|
-
return dx > 0 ? 'left' : 'right';
|
|
240
|
-
return dy > 0 ? 'top' : 'bottom';
|
|
241
|
-
}
|
package/dist/layout.d.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LayoutEngine — the pluggable layout contract. A LayoutEngine is a pure
|
|
3
|
-
* function from PlantUmlModel to concrete positions; the parser never talks
|
|
4
|
-
* to a layout engine and the renderer never talks to the parser, so swapping
|
|
5
|
-
* engines (elkjs, @viz-js/viz, a custom tuner) is an injection, not a rewrite.
|
|
6
|
-
*/
|
|
7
|
-
import { PlantUmlModel, PlantUmlEntity } from './model.js';
|
|
8
|
-
export interface Point {
|
|
9
|
-
x: number;
|
|
10
|
-
y: number;
|
|
11
|
-
}
|
|
12
|
-
export interface NodeLayout {
|
|
13
|
-
id: string;
|
|
14
|
-
x: number;
|
|
15
|
-
y: number;
|
|
16
|
-
width: number;
|
|
17
|
-
height: number;
|
|
18
|
-
}
|
|
19
|
-
export interface EdgeLayout {
|
|
20
|
-
id: string;
|
|
21
|
-
source: string;
|
|
22
|
-
target: string;
|
|
23
|
-
points: Point[];
|
|
24
|
-
}
|
|
25
|
-
export interface PackageLayout {
|
|
26
|
-
id: string;
|
|
27
|
-
x: number;
|
|
28
|
-
y: number;
|
|
29
|
-
width: number;
|
|
30
|
-
height: number;
|
|
31
|
-
}
|
|
32
|
-
export interface LayoutResult {
|
|
33
|
-
nodes: NodeLayout[];
|
|
34
|
-
edges: EdgeLayout[];
|
|
35
|
-
/** Package frames; engines without container support may omit this. */
|
|
36
|
-
packages?: PackageLayout[];
|
|
37
|
-
width: number;
|
|
38
|
-
height: number;
|
|
39
|
-
}
|
|
40
|
-
export interface LayoutEngine {
|
|
41
|
-
layout(model: PlantUmlModel): Promise<LayoutResult>;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Shared drawing metrics. The renderer reads these so entity sizing and the
|
|
45
|
-
* emitted SVG stay in lockstep: the font family and size are bound to the same
|
|
46
|
-
* monospace advance [[../text-measure]] measures with.
|
|
47
|
-
*/
|
|
48
|
-
export declare const METRICS: {
|
|
49
|
-
readonly fontFamily: "monospace";
|
|
50
|
-
readonly fontSize: 13;
|
|
51
|
-
readonly lineHeight: 18;
|
|
52
|
-
readonly paddingX: 10;
|
|
53
|
-
readonly paddingY: 8;
|
|
54
|
-
readonly minWidth: 60;
|
|
55
|
-
};
|
|
56
|
-
/**
|
|
57
|
-
* Package label-tab geometry, shared by the renderer and the layout-metrics
|
|
58
|
-
* overlap counter so a drawn tab and a measured tab are the same rectangle.
|
|
59
|
-
*/
|
|
60
|
-
export declare const PACKAGE_LABEL: {
|
|
61
|
-
readonly tabHeight: 18;
|
|
62
|
-
readonly fontSize: 12;
|
|
63
|
-
readonly tabPadding: 12;
|
|
64
|
-
};
|
|
65
|
-
/** Reconstruct the name drawn in the title compartment, generics expanded. */
|
|
66
|
-
export declare function displayName(entity: PlantUmlEntity): string;
|
|
67
|
-
/**
|
|
68
|
-
* Approximate the on-canvas size of an entity box. The title plus every member
|
|
69
|
-
* line contributes one row; the width is the longest rendered line measured
|
|
70
|
-
* through the text table plus padding, never below MIN_WIDTH.
|
|
71
|
-
*/
|
|
72
|
-
export declare function estimateEntitySize(entity: PlantUmlEntity): {
|
|
73
|
-
width: number;
|
|
74
|
-
height: number;
|
|
75
|
-
};
|
package/dist/layout.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LayoutEngine — the pluggable layout contract. A LayoutEngine is a pure
|
|
3
|
-
* function from PlantUmlModel to concrete positions; the parser never talks
|
|
4
|
-
* to a layout engine and the renderer never talks to the parser, so swapping
|
|
5
|
-
* engines (elkjs, @viz-js/viz, a custom tuner) is an injection, not a rewrite.
|
|
6
|
-
*/
|
|
7
|
-
import { measureTextWidth, MONOSPACE_FONT_SIZE } from './text-measure.js';
|
|
8
|
-
/**
|
|
9
|
-
* Shared drawing metrics. The renderer reads these so entity sizing and the
|
|
10
|
-
* emitted SVG stay in lockstep: the font family and size are bound to the same
|
|
11
|
-
* monospace advance [[../text-measure]] measures with.
|
|
12
|
-
*/
|
|
13
|
-
export const METRICS = {
|
|
14
|
-
fontFamily: 'monospace',
|
|
15
|
-
fontSize: MONOSPACE_FONT_SIZE,
|
|
16
|
-
lineHeight: 18,
|
|
17
|
-
paddingX: 10,
|
|
18
|
-
paddingY: 8,
|
|
19
|
-
minWidth: 60,
|
|
20
|
-
};
|
|
21
|
-
/**
|
|
22
|
-
* Package label-tab geometry, shared by the renderer and the layout-metrics
|
|
23
|
-
* overlap counter so a drawn tab and a measured tab are the same rectangle.
|
|
24
|
-
*/
|
|
25
|
-
export const PACKAGE_LABEL = {
|
|
26
|
-
tabHeight: 18,
|
|
27
|
-
fontSize: 12,
|
|
28
|
-
tabPadding: 12,
|
|
29
|
-
};
|
|
30
|
-
/** Reconstruct the name drawn in the title compartment, generics expanded. */
|
|
31
|
-
export function displayName(entity) {
|
|
32
|
-
const generics = entity.generics;
|
|
33
|
-
if (!generics || generics.length === 0)
|
|
34
|
-
return entity.name;
|
|
35
|
-
return `${entity.name}<${generics.join(', ')}>`;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Approximate the on-canvas size of an entity box. The title plus every member
|
|
39
|
-
* line contributes one row; the width is the longest rendered line measured
|
|
40
|
-
* through the text table plus padding, never below MIN_WIDTH.
|
|
41
|
-
*/
|
|
42
|
-
export function estimateEntitySize(entity) {
|
|
43
|
-
const lines = [
|
|
44
|
-
displayName(entity),
|
|
45
|
-
...entity.members.map((m) => m.raw),
|
|
46
|
-
];
|
|
47
|
-
const longest = lines.reduce((max, line) => Math.max(max, measureTextWidth(line, METRICS.fontSize)), 0);
|
|
48
|
-
const width = Math.max(METRICS.minWidth, longest + METRICS.paddingX * 2);
|
|
49
|
-
const height = lines.length * METRICS.lineHeight + METRICS.paddingY * 2;
|
|
50
|
-
return { width, height };
|
|
51
|
-
}
|
package/dist/parser.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Hand-written recursive-descent parser for the PlantUML class-diagram subset.
|
|
3
|
-
*
|
|
4
|
-
* M2 scope adds members (visibility `-`/`+`/`#`/`~`, fields, method
|
|
5
|
-
* signatures), relations (inheritance / implementation / dependency /
|
|
6
|
-
* association / aggregation / composition with cardinality and labels),
|
|
7
|
-
* generics (`List~Item~`, `BaseEntity<T>`), package/namespace grouping,
|
|
8
|
-
* aliases, and tolerant skipping of skinparam/hide/note directives.
|
|
9
|
-
*/
|
|
10
|
-
import { PlantUmlModel } from './model.js';
|
|
11
|
-
/**
|
|
12
|
-
* Parse a PlantUML source string into a PlantUmlModel.
|
|
13
|
-
*
|
|
14
|
-
* The parser is total: unrecognised lines (skinparam, hide, note, title,
|
|
15
|
-
* legend, …) are tolerated and skipped rather than failing the parse.
|
|
16
|
-
*/
|
|
17
|
-
export declare function parsePlantUml(source: string): PlantUmlModel;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|