zombie-mermaid 4.2.2 → 4.2.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/package.json +9 -7
- package/src/__tests__/architecture-integration.test.ts +0 -217
- package/src/__tests__/ascii-ansi.test.ts +0 -539
- package/src/__tests__/ascii-draw-bundles.test.ts +0 -849
- package/src/__tests__/ascii-edge-bundling-routing.test.ts +0 -214
- package/src/__tests__/ascii-edge-cross-style-overlap.test.ts +0 -216
- package/src/__tests__/ascii-grid-deep-chain.test.ts +0 -64
- package/src/__tests__/ascii-hyperlinks.test.ts +0 -364
- package/src/__tests__/ascii-nested-subgraph-sibling-order-444.test.ts +0 -108
- package/src/__tests__/ascii-nonmember-beside-frame-1283.test.ts +0 -113
- package/src/__tests__/ascii-package-boundary.test.ts +0 -291
- package/src/__tests__/ascii-parallel-edges-329.test.ts +0 -315
- package/src/__tests__/ascii-parallel-lane-border-1230.test.ts +0 -71
- package/src/__tests__/ascii-reverse-direction-edges-629.test.ts +0 -282
- package/src/__tests__/ascii-stacked-frame-geometry-1285.test.ts +0 -107
- package/src/__tests__/ascii-subgraph-bbox.test.ts +0 -139
- package/src/__tests__/ascii-subgraph-later-edgeless-301.test.ts +0 -277
- package/src/__tests__/browser.test.ts +0 -34
- package/src/__tests__/c4-integration.test.ts +0 -161
- package/src/__tests__/c4-orientation.test.ts +0 -162
- package/src/__tests__/c4-upstream-integration.test.ts +0 -211
- package/src/__tests__/class-click-interactions.test.ts +0 -232
- package/src/__tests__/class-integration.test.ts +0 -154
- package/src/__tests__/class-notes.test.ts +0 -317
- package/src/__tests__/class-parser-error-messages.test.ts +0 -162
- package/src/__tests__/class-parser.test.ts +0 -347
- package/src/__tests__/class-styling.test.ts +0 -485
- package/src/__tests__/cli-diagrams.test.ts +0 -106
- package/src/__tests__/cli-e2e.test.ts +0 -275
- package/src/__tests__/cli-entrypoint.test.ts +0 -146
- package/src/__tests__/cli-mcp-dispatch.test.ts +0 -68
- package/src/__tests__/cli-mcp.test.ts +0 -44
- package/src/__tests__/cli-png.test.ts +0 -35
- package/src/__tests__/cli-render.test.ts +0 -988
- package/src/__tests__/cli-test-helpers.ts +0 -54
- package/src/__tests__/cli-web-dispatch.test.ts +0 -62
- package/src/__tests__/cli-web.test.ts +0 -203
- package/src/__tests__/cli.test.ts +0 -1027
- package/src/__tests__/compound-flat-layout.test.ts +0 -901
- package/src/__tests__/direction-override.test.ts +0 -397
- package/src/__tests__/edge-approach-direction.test.ts +0 -295
- package/src/__tests__/edge-bundling-node-clearance.test.ts +0 -179
- package/src/__tests__/elk-layout-cache.test.ts +0 -223
- package/src/__tests__/er-click-unsupported.test.ts +0 -69
- package/src/__tests__/er-integration.test.ts +0 -531
- package/src/__tests__/er-parser-error-messages.test.ts +0 -82
- package/src/__tests__/er-parser.test.ts +0 -294
- package/src/__tests__/expanded-shapes.test.ts +0 -229
- package/src/__tests__/flowchart-back-edge-prereversal-1239.test.ts +0 -212
- package/src/__tests__/flowchart-syntax-gaps.test.ts +0 -371
- package/src/__tests__/guides-examples.test.ts +0 -181
- package/src/__tests__/integration.test.ts +0 -729
- package/src/__tests__/interactivity-config.test.ts +0 -723
- package/src/__tests__/interactivity-option.test.ts +0 -173
- package/src/__tests__/layout-disconnected.test.ts +0 -384
- package/src/__tests__/layout-engine.test.ts +0 -91
- package/src/__tests__/linkstyle.test.ts +0 -133
- package/src/__tests__/mcp-render-ascii-tool.test.ts +0 -95
- package/src/__tests__/mcp-render-svg-theme-guard.test.ts +0 -36
- package/src/__tests__/mcp-render-svg-tool.test.ts +0 -118
- package/src/__tests__/mermaid-entity-codes.test.ts +0 -114
- package/src/__tests__/monospace-metrics.test.ts +0 -74
- package/src/__tests__/multiline-labels.test.ts +0 -924
- package/src/__tests__/nested-subgraph-direction-crossing.test.ts +0 -322
- package/src/__tests__/nested-subgraph-edge-container-offset.test.ts +0 -151
- package/src/__tests__/package-info.test.ts +0 -51
- package/src/__tests__/parser.test.ts +0 -1544
- package/src/__tests__/render-options-overrides.test.ts +0 -257
- package/src/__tests__/render-options-per-type.test.ts +0 -178
- package/src/__tests__/renderer.test.ts +0 -743
- package/src/__tests__/resolve-colors.test.ts +0 -448
- package/src/__tests__/self-loop-edges.test.ts +0 -112
- package/src/__tests__/sequence-activation-check.test.ts +0 -219
- package/src/__tests__/sequence-integration.test.ts +0 -312
- package/src/__tests__/sequence-layout.test.ts +0 -1103
- package/src/__tests__/sequence-parser-syntax-errors.test.ts +0 -179
- package/src/__tests__/sequence-parser.test.ts +0 -821
- package/src/__tests__/start-marker-only-links-1386.test.ts +0 -104
- package/src/__tests__/state-composite-order-1287.test.ts +0 -132
- package/src/__tests__/state-flow-order-1240.test.ts +0 -113
- package/src/__tests__/state-styling.test.ts +0 -150
- package/src/__tests__/statements.test.ts +0 -264
- package/src/__tests__/styles.test.ts +0 -220
- package/src/__tests__/subgraph-title-edge-gap-1239.test.ts +0 -238
- package/src/__tests__/svg-accessible-name-conformance.test.ts +0 -276
- package/src/__tests__/svg-accessible-name.test.ts +0 -421
- package/src/__tests__/svg-csp-style-options.test.ts +0 -427
- package/src/__tests__/workspace-package-boundaries.test.ts +0 -336
- package/src/__tests__/xychart-integration.test.ts +0 -137
- package/src/__tests__/xychart-layout.test.ts +0 -205
- package/src/__tests__/xychart-parser-categories.test.ts +0 -78
- package/src/__tests__/xychart-parser-errors.test.ts +0 -95
|
@@ -1,849 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Coverage for src/ascii/draw-bundles.ts — fan-in/fan-out edge bundle
|
|
3
|
-
* rendering (shared trunks, junction characters, box-start connectors,
|
|
4
|
-
* arrowheads).
|
|
5
|
-
*
|
|
6
|
-
* Many branches in this file are unreachable through any real Mermaid
|
|
7
|
-
* diagram: edge-bundling.ts only creates bundles for TD graphs, and by
|
|
8
|
-
* construction always aligns a bundle's junction/shared-trunk x (or y)
|
|
9
|
-
* coordinate with the shared node's, so the shared trunk is always a
|
|
10
|
-
* straight line and a fan-in edge always exits its source moving "down".
|
|
11
|
-
* The tests below build a real bundle via the actual layout pipeline
|
|
12
|
-
* (so canvas/grid state is authentic) and then mutate its path arrays to
|
|
13
|
-
* exercise the geometry this file is documented to handle but that never
|
|
14
|
-
* occurs today — verifying the drawing functions still do the right thing
|
|
15
|
-
* with that input.
|
|
16
|
-
*/
|
|
17
|
-
import { describe, it, expect } from 'vitest'
|
|
18
|
-
import { parseMermaid } from '@zombie-mermaid/mermaid-parser'
|
|
19
|
-
import { convertToAsciiGraph } from '../../packages/ascii-renderer/src/converter.ts'
|
|
20
|
-
import { createMapping } from '../../packages/ascii-renderer/src/grid.ts'
|
|
21
|
-
import type {
|
|
22
|
-
AsciiConfig,
|
|
23
|
-
AsciiGraph,
|
|
24
|
-
EdgeBundle,
|
|
25
|
-
GridCoord,
|
|
26
|
-
} from '../../packages/ascii-renderer/src/types.ts'
|
|
27
|
-
import {
|
|
28
|
-
drawBundledEdgeSegment,
|
|
29
|
-
drawBundleSharedPath,
|
|
30
|
-
drawBundleArrowhead,
|
|
31
|
-
drawBundleArrowheadStart,
|
|
32
|
-
drawBundledEdgeArrowhead,
|
|
33
|
-
drawBundledEdgeArrowheadStart,
|
|
34
|
-
drawJunctionCharacter,
|
|
35
|
-
} from '../../packages/ascii-renderer/src/draw-bundles.ts'
|
|
36
|
-
import { renderMermaidASCII } from '@zombie-mermaid/ascii-renderer'
|
|
37
|
-
|
|
38
|
-
function buildGraph(src: string, useAscii = false): AsciiGraph {
|
|
39
|
-
const config: AsciiConfig = {
|
|
40
|
-
useAscii,
|
|
41
|
-
paddingX: 5,
|
|
42
|
-
paddingY: 5,
|
|
43
|
-
boxBorderPadding: 1,
|
|
44
|
-
graphDirection: 'TD',
|
|
45
|
-
}
|
|
46
|
-
const graph = convertToAsciiGraph(parseMermaid(src), config)
|
|
47
|
-
createMapping(graph)
|
|
48
|
-
return graph
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** Every non-space character on the canvas, in scan order. */
|
|
52
|
-
function nonSpaceChars(canvas: string[][]): string {
|
|
53
|
-
const found: string[] = []
|
|
54
|
-
for (const col of canvas) {
|
|
55
|
-
for (const ch of col) {
|
|
56
|
-
if (ch !== ' ') found.push(ch)
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return found.join('')
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const FAN_IN = `flowchart TD
|
|
63
|
-
A[One] --> C[Target]
|
|
64
|
-
B[Two] --> C`
|
|
65
|
-
|
|
66
|
-
// A plain fan-in centers its target between the sources, so the grid has no
|
|
67
|
-
// spare columns beside the junction. The hand-built trunk paths below step a
|
|
68
|
-
// column to either side of it; a source that feeds a second node keeps the
|
|
69
|
-
// target under the first source (#1339), leaving that room.
|
|
70
|
-
const FAN_IN_BESIDE = `flowchart TD
|
|
71
|
-
A[One] --> C[Target]
|
|
72
|
-
B[Two] --> C
|
|
73
|
-
B --> D[Other]`
|
|
74
|
-
|
|
75
|
-
const FAN_OUT = `flowchart TD
|
|
76
|
-
C[Source] --> A[One]
|
|
77
|
-
C --> B[Two]`
|
|
78
|
-
|
|
79
|
-
const BIDIRECTIONAL_FAN_IN = `flowchart TD
|
|
80
|
-
A[One] <--> C[Target]
|
|
81
|
-
B[Two] <--> C`
|
|
82
|
-
|
|
83
|
-
const BIDIRECTIONAL_FAN_OUT = `flowchart TD
|
|
84
|
-
C[Source] <--> A[One]
|
|
85
|
-
C <--> B[Two]`
|
|
86
|
-
|
|
87
|
-
const MIXED_FAN_OUT = `flowchart TD
|
|
88
|
-
C[Source] --> A[One]
|
|
89
|
-
C[Source] <--> B[Two]`
|
|
90
|
-
|
|
91
|
-
describe('draw-bundles: rendered fan-in/fan-out diagrams', () => {
|
|
92
|
-
it('draws a fan-in bundle with a T-junction and single arrowhead (unicode)', () => {
|
|
93
|
-
const out = renderMermaidASCII(FAN_IN, { useAscii: false })
|
|
94
|
-
// The two sources join in one trunk whose T-junction sits over the
|
|
95
|
-
// (centered) target.
|
|
96
|
-
expect(out).toContain('└───────┬──────┘')
|
|
97
|
-
expect(out).toContain('▼')
|
|
98
|
-
expect(out.match(/▼/g)).toHaveLength(1)
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
it('draws a fan-in bundle box-start and junction in ASCII charset', () => {
|
|
102
|
-
const out = renderMermaidASCII(FAN_IN, { useAscii: true })
|
|
103
|
-
expect(out).toContain('+-------+------+')
|
|
104
|
-
expect(out).toContain('+--+--+')
|
|
105
|
-
expect(out).toContain('+--------+')
|
|
106
|
-
expect(out).toContain('v')
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
it('draws a fan-out bundle with one arrowhead per target (unicode)', () => {
|
|
110
|
-
const out = renderMermaidASCII(FAN_OUT, { useAscii: false })
|
|
111
|
-
// The source sits centred over its two targets, so the shared trunk
|
|
112
|
-
// leaves its bottom border and splits at a ┴ directly beneath it.
|
|
113
|
-
expect(out).toMatch(/┌─+┴─+┐/)
|
|
114
|
-
expect(out.match(/▼/g)).toHaveLength(2)
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
it('draws a fan-out bundle with one arrowhead per target (ASCII)', () => {
|
|
118
|
-
const out = renderMermaidASCII(FAN_OUT, { useAscii: true })
|
|
119
|
-
expect(out.match(/v/g)).toHaveLength(2)
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
it('draws a start arrowhead at each source for a bidirectional fan-in bundle', () => {
|
|
123
|
-
const out = renderMermaidASCII(BIDIRECTIONAL_FAN_IN, { useAscii: false })
|
|
124
|
-
// One end arrowhead at the shared target, plus one start arrowhead per source.
|
|
125
|
-
expect(out.match(/▼/g)).toHaveLength(1)
|
|
126
|
-
expect(out.match(/▲/g)).toHaveLength(2)
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
it('draws a single shared start arrowhead for a bidirectional fan-out bundle', () => {
|
|
130
|
-
const out = renderMermaidASCII(BIDIRECTIONAL_FAN_OUT, { useAscii: false })
|
|
131
|
-
// One end arrowhead per target, plus a single shared start arrowhead at the source.
|
|
132
|
-
expect(out.match(/▼/g)).toHaveLength(2)
|
|
133
|
-
expect(out.match(/▲/g)).toHaveLength(1)
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
it('omits the shared fan-out start arrowhead when the bundled edges disagree on hasArrowStart', () => {
|
|
137
|
-
const out = renderMermaidASCII(MIXED_FAN_OUT, { useAscii: false })
|
|
138
|
-
expect(out.match(/▼/g)).toHaveLength(2)
|
|
139
|
-
expect(out).not.toContain('▲')
|
|
140
|
-
})
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
describe('drawBundledEdgeSegment', () => {
|
|
144
|
-
const graph = buildGraph(FAN_IN)
|
|
145
|
-
const bundle = graph.bundles[0]!
|
|
146
|
-
const junction = bundle.junctionPoint!
|
|
147
|
-
const edgeTemplate = bundle.edges[0]!
|
|
148
|
-
|
|
149
|
-
it('returns unmodified canvases when pathToJunction is empty', () => {
|
|
150
|
-
const edge = { ...edgeTemplate, pathToJunction: [] }
|
|
151
|
-
const result = drawBundledEdgeSegment(graph, edge, bundle)
|
|
152
|
-
for (const canvas of result) {
|
|
153
|
-
expect(nonSpaceChars(canvas)).toBe('')
|
|
154
|
-
}
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
it('returns unmodified canvases when pathToJunction is undefined', () => {
|
|
158
|
-
const edge = { ...edgeTemplate, pathToJunction: undefined }
|
|
159
|
-
const result = drawBundledEdgeSegment(graph, edge, bundle)
|
|
160
|
-
for (const canvas of result) {
|
|
161
|
-
expect(nonSpaceChars(canvas)).toBe('')
|
|
162
|
-
}
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
it.each<[string, GridCoord[], string]>([
|
|
166
|
-
[
|
|
167
|
-
'left-then-down bend',
|
|
168
|
-
[
|
|
169
|
-
{ x: junction.x + 4, y: junction.y - 1 },
|
|
170
|
-
{ x: junction.x, y: junction.y - 1 },
|
|
171
|
-
{ x: junction.x, y: junction.y },
|
|
172
|
-
],
|
|
173
|
-
'┌',
|
|
174
|
-
],
|
|
175
|
-
[
|
|
176
|
-
'up-then-right bend',
|
|
177
|
-
[
|
|
178
|
-
{ x: junction.x, y: junction.y + 4 },
|
|
179
|
-
{ x: junction.x, y: junction.y - 1 },
|
|
180
|
-
{ x: junction.x + 4, y: junction.y - 1 },
|
|
181
|
-
],
|
|
182
|
-
'┌',
|
|
183
|
-
],
|
|
184
|
-
[
|
|
185
|
-
'down-then-right bend',
|
|
186
|
-
[
|
|
187
|
-
{ x: junction.x, y: junction.y - 2 },
|
|
188
|
-
{ x: junction.x, y: junction.y - 1 },
|
|
189
|
-
{ x: junction.x + 4, y: junction.y - 1 },
|
|
190
|
-
],
|
|
191
|
-
'└',
|
|
192
|
-
],
|
|
193
|
-
[
|
|
194
|
-
'left-then-up bend',
|
|
195
|
-
[
|
|
196
|
-
{ x: junction.x + 4, y: junction.y - 1 },
|
|
197
|
-
{ x: junction.x, y: junction.y - 1 },
|
|
198
|
-
{ x: junction.x, y: junction.y - 5 },
|
|
199
|
-
],
|
|
200
|
-
'└',
|
|
201
|
-
],
|
|
202
|
-
[
|
|
203
|
-
'right-then-down bend',
|
|
204
|
-
[
|
|
205
|
-
{ x: junction.x - 4, y: junction.y - 1 },
|
|
206
|
-
{ x: junction.x, y: junction.y - 1 },
|
|
207
|
-
{ x: junction.x, y: junction.y },
|
|
208
|
-
],
|
|
209
|
-
'┐',
|
|
210
|
-
],
|
|
211
|
-
[
|
|
212
|
-
'up-then-left bend',
|
|
213
|
-
[
|
|
214
|
-
{ x: junction.x, y: junction.y + 4 },
|
|
215
|
-
{ x: junction.x, y: junction.y - 1 },
|
|
216
|
-
{ x: junction.x - 4, y: junction.y - 1 },
|
|
217
|
-
],
|
|
218
|
-
'┐',
|
|
219
|
-
],
|
|
220
|
-
[
|
|
221
|
-
'right-then-up bend',
|
|
222
|
-
[
|
|
223
|
-
{ x: junction.x - 4, y: junction.y - 1 },
|
|
224
|
-
{ x: junction.x, y: junction.y - 1 },
|
|
225
|
-
{ x: junction.x, y: junction.y - 5 },
|
|
226
|
-
],
|
|
227
|
-
'┘',
|
|
228
|
-
],
|
|
229
|
-
[
|
|
230
|
-
'down-then-left bend',
|
|
231
|
-
[
|
|
232
|
-
{ x: junction.x, y: junction.y - 2 },
|
|
233
|
-
{ x: junction.x, y: junction.y - 1 },
|
|
234
|
-
{ x: junction.x - 4, y: junction.y - 1 },
|
|
235
|
-
],
|
|
236
|
-
'┘',
|
|
237
|
-
],
|
|
238
|
-
[
|
|
239
|
-
'straight run (no real turn)',
|
|
240
|
-
[
|
|
241
|
-
{ x: junction.x, y: junction.y - 2 },
|
|
242
|
-
{ x: junction.x, y: junction.y - 1 },
|
|
243
|
-
{ x: junction.x, y: junction.y },
|
|
244
|
-
],
|
|
245
|
-
'+',
|
|
246
|
-
],
|
|
247
|
-
])('draws the %s corner', (_label, path, expectedChar) => {
|
|
248
|
-
const edge = { ...edgeTemplate, pathToJunction: path }
|
|
249
|
-
const [, , , , cornersCanvas] = drawBundledEdgeSegment(graph, edge, bundle)
|
|
250
|
-
expect(nonSpaceChars(cornersCanvas)).toBe(expectedChar)
|
|
251
|
-
})
|
|
252
|
-
|
|
253
|
-
it.each<[string, GridCoord[], string]>([
|
|
254
|
-
[
|
|
255
|
-
'Up',
|
|
256
|
-
[
|
|
257
|
-
{ x: junction.x, y: junction.y + 3 },
|
|
258
|
-
{ x: junction.x, y: junction.y },
|
|
259
|
-
],
|
|
260
|
-
'┴',
|
|
261
|
-
],
|
|
262
|
-
[
|
|
263
|
-
'Left',
|
|
264
|
-
[
|
|
265
|
-
{ x: junction.x + 4, y: junction.y },
|
|
266
|
-
{ x: junction.x, y: junction.y },
|
|
267
|
-
],
|
|
268
|
-
'┤',
|
|
269
|
-
],
|
|
270
|
-
[
|
|
271
|
-
'Right',
|
|
272
|
-
[
|
|
273
|
-
{ x: junction.x - 4, y: junction.y },
|
|
274
|
-
{ x: junction.x + 4, y: junction.y },
|
|
275
|
-
],
|
|
276
|
-
'├',
|
|
277
|
-
],
|
|
278
|
-
])(
|
|
279
|
-
'draws a %s-direction box-start connector',
|
|
280
|
-
(_label, path, expectedChar) => {
|
|
281
|
-
const edge = { ...edgeTemplate, pathToJunction: path }
|
|
282
|
-
const [, boxStartCanvas] = drawBundledEdgeSegment(graph, edge, bundle)
|
|
283
|
-
expect(nonSpaceChars(boxStartCanvas)).toBe(expectedChar)
|
|
284
|
-
},
|
|
285
|
-
)
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
describe('drawBundleSharedPath', () => {
|
|
289
|
-
it('returns unmodified canvases when sharedPath has fewer than 2 points', () => {
|
|
290
|
-
const graph = buildGraph(FAN_IN_BESIDE)
|
|
291
|
-
const bundle = graph.bundles[0]!
|
|
292
|
-
const shortBundle = {
|
|
293
|
-
...bundle,
|
|
294
|
-
sharedPath: [bundle.junctionPoint!],
|
|
295
|
-
}
|
|
296
|
-
const [pathCanvas, cornersCanvas] = drawBundleSharedPath(graph, shortBundle)
|
|
297
|
-
expect(nonSpaceChars(pathCanvas)).toBe('')
|
|
298
|
-
expect(nonSpaceChars(cornersCanvas)).toBe('')
|
|
299
|
-
})
|
|
300
|
-
|
|
301
|
-
it.each<[string, (j: GridCoord) => GridCoord[], string]>([
|
|
302
|
-
[
|
|
303
|
-
'right-then-down bend',
|
|
304
|
-
(j) => [j, { x: j.x + 4, y: j.y }, { x: j.x + 4, y: j.y + 1 }],
|
|
305
|
-
'┐',
|
|
306
|
-
],
|
|
307
|
-
[
|
|
308
|
-
'right-then-up bend',
|
|
309
|
-
(j) => [
|
|
310
|
-
{ x: j.x - 4, y: j.y + 1 },
|
|
311
|
-
{ x: j.x, y: j.y + 1 },
|
|
312
|
-
{ x: j.x, y: j.y },
|
|
313
|
-
],
|
|
314
|
-
'┘',
|
|
315
|
-
],
|
|
316
|
-
[
|
|
317
|
-
'up-then-right bend',
|
|
318
|
-
(j) => [
|
|
319
|
-
{ x: j.x, y: j.y + 2 },
|
|
320
|
-
{ x: j.x, y: j.y + 1 },
|
|
321
|
-
{ x: j.x + 4, y: j.y + 1 },
|
|
322
|
-
],
|
|
323
|
-
'┌',
|
|
324
|
-
],
|
|
325
|
-
[
|
|
326
|
-
'left-then-up bend',
|
|
327
|
-
(j) => [{ x: j.x + 4, y: j.y }, j, { x: j.x, y: j.y - 1 }],
|
|
328
|
-
'└',
|
|
329
|
-
],
|
|
330
|
-
[
|
|
331
|
-
'straight run (no real turn)',
|
|
332
|
-
(j) => [j, { x: j.x, y: j.y + 1 }, { x: j.x, y: j.y + 2 }],
|
|
333
|
-
'+',
|
|
334
|
-
],
|
|
335
|
-
])(
|
|
336
|
-
'draws a %s in the shared trunk (unicode)',
|
|
337
|
-
(_label, makePath, expectedChar) => {
|
|
338
|
-
const graph = buildGraph(FAN_IN_BESIDE)
|
|
339
|
-
const bundle = graph.bundles[0]!
|
|
340
|
-
const bent = {
|
|
341
|
-
...bundle,
|
|
342
|
-
sharedPath: makePath(bundle.junctionPoint!),
|
|
343
|
-
}
|
|
344
|
-
const [, cornersCanvas] = drawBundleSharedPath(graph, bent)
|
|
345
|
-
expect(nonSpaceChars(cornersCanvas)).toBe(expectedChar)
|
|
346
|
-
},
|
|
347
|
-
)
|
|
348
|
-
|
|
349
|
-
it('draws a bend in the shared trunk as "+" in ASCII charset', () => {
|
|
350
|
-
const graph = buildGraph(FAN_IN_BESIDE, true)
|
|
351
|
-
const bundle = graph.bundles[0]!
|
|
352
|
-
const junction = bundle.junctionPoint!
|
|
353
|
-
const bent = {
|
|
354
|
-
...bundle,
|
|
355
|
-
sharedPath: [
|
|
356
|
-
junction,
|
|
357
|
-
{ x: junction.x + 4, y: junction.y },
|
|
358
|
-
{ x: junction.x + 4, y: junction.y + 1 },
|
|
359
|
-
],
|
|
360
|
-
}
|
|
361
|
-
const [, cornersCanvas] = drawBundleSharedPath(graph, bent)
|
|
362
|
-
expect(nonSpaceChars(cornersCanvas)).toBe('+')
|
|
363
|
-
})
|
|
364
|
-
})
|
|
365
|
-
|
|
366
|
-
/** A 2-point path whose last segment arrives from the given direction. */
|
|
367
|
-
function arrivalSharedPath(dir: 'Up' | 'Left' | 'Right'): GridCoord[] {
|
|
368
|
-
switch (dir) {
|
|
369
|
-
case 'Up':
|
|
370
|
-
return [
|
|
371
|
-
{ x: 1, y: 6 },
|
|
372
|
-
{ x: 1, y: 4 },
|
|
373
|
-
]
|
|
374
|
-
case 'Left':
|
|
375
|
-
return [
|
|
376
|
-
{ x: 5, y: 4 },
|
|
377
|
-
{ x: 1, y: 4 },
|
|
378
|
-
]
|
|
379
|
-
case 'Right':
|
|
380
|
-
return [
|
|
381
|
-
{ x: 1, y: 4 },
|
|
382
|
-
{ x: 5, y: 4 },
|
|
383
|
-
]
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
describe('drawBundleArrowhead', () => {
|
|
388
|
-
const graph = buildGraph(FAN_IN)
|
|
389
|
-
const bundle = graph.bundles[0]!
|
|
390
|
-
|
|
391
|
-
it('returns an unmodified canvas when sharedPath has fewer than 2 points', () => {
|
|
392
|
-
const shortBundle = {
|
|
393
|
-
...bundle,
|
|
394
|
-
sharedPath: [bundle.junctionPoint!],
|
|
395
|
-
}
|
|
396
|
-
const canvas = drawBundleArrowhead(graph, shortBundle)
|
|
397
|
-
expect(nonSpaceChars(canvas)).toBe('')
|
|
398
|
-
})
|
|
399
|
-
|
|
400
|
-
it.each<['Up' | 'Left' | 'Right', string]>([
|
|
401
|
-
['Up', '▲'],
|
|
402
|
-
['Left', '◄'],
|
|
403
|
-
['Right', '►'],
|
|
404
|
-
])(
|
|
405
|
-
'draws a %s-arriving trunk as the %s arrowhead (unicode)',
|
|
406
|
-
(dir, expectedChar) => {
|
|
407
|
-
const b = { ...bundle, sharedPath: arrivalSharedPath(dir) }
|
|
408
|
-
const canvas = drawBundleArrowhead(graph, b)
|
|
409
|
-
expect(nonSpaceChars(canvas)).toBe(expectedChar)
|
|
410
|
-
},
|
|
411
|
-
)
|
|
412
|
-
|
|
413
|
-
it.each<['Up' | 'Left' | 'Right', string]>([
|
|
414
|
-
['Up', '^'],
|
|
415
|
-
['Left', '<'],
|
|
416
|
-
['Right', '>'],
|
|
417
|
-
])(
|
|
418
|
-
'draws a %s-arriving trunk as the %s arrowhead (ASCII)',
|
|
419
|
-
(dir, expectedChar) => {
|
|
420
|
-
const asciiGraph = buildGraph(FAN_IN, true)
|
|
421
|
-
const asciiBundle = asciiGraph.bundles[0]!
|
|
422
|
-
const b = {
|
|
423
|
-
...asciiBundle,
|
|
424
|
-
sharedPath: arrivalSharedPath(dir),
|
|
425
|
-
}
|
|
426
|
-
const canvas = drawBundleArrowhead(asciiGraph, b)
|
|
427
|
-
expect(nonSpaceChars(canvas)).toBe(expectedChar)
|
|
428
|
-
},
|
|
429
|
-
)
|
|
430
|
-
|
|
431
|
-
it('falls back to the default arrowhead for a diagonal trunk direction (unicode)', () => {
|
|
432
|
-
const b = {
|
|
433
|
-
...bundle,
|
|
434
|
-
sharedPath: [
|
|
435
|
-
{ x: 1, y: 2 },
|
|
436
|
-
{ x: 5, y: 4 },
|
|
437
|
-
],
|
|
438
|
-
}
|
|
439
|
-
const canvas = drawBundleArrowhead(graph, b)
|
|
440
|
-
expect(nonSpaceChars(canvas)).toBe('▼')
|
|
441
|
-
})
|
|
442
|
-
|
|
443
|
-
it('falls back to the default arrowhead for a diagonal trunk direction (ASCII)', () => {
|
|
444
|
-
const asciiGraph = buildGraph(FAN_IN, true)
|
|
445
|
-
const asciiBundle = asciiGraph.bundles[0]!
|
|
446
|
-
const b = {
|
|
447
|
-
...asciiBundle,
|
|
448
|
-
sharedPath: [
|
|
449
|
-
{ x: 1, y: 2 },
|
|
450
|
-
{ x: 5, y: 4 },
|
|
451
|
-
],
|
|
452
|
-
}
|
|
453
|
-
const canvas = drawBundleArrowhead(asciiGraph, b)
|
|
454
|
-
expect(nonSpaceChars(canvas)).toBe('v')
|
|
455
|
-
})
|
|
456
|
-
|
|
457
|
-
it('offsets the arrowhead horizontally for an LR graph direction', () => {
|
|
458
|
-
// Bundling never fires for LR graphs (see edge-bundling.ts), so this
|
|
459
|
-
// exercises the function's LR branch directly rather than through a
|
|
460
|
-
// real diagram. The shared node is shifted away from the canvas edge
|
|
461
|
-
// first since its real (TD) attachment point sits at x=0, and the LR
|
|
462
|
-
// offset (`dc.x -= 1`) would otherwise underflow the canvas.
|
|
463
|
-
const shiftedNode = {
|
|
464
|
-
...bundle.sharedNode,
|
|
465
|
-
drawingCoord: {
|
|
466
|
-
...bundle.sharedNode.drawingCoord!,
|
|
467
|
-
x: bundle.sharedNode.drawingCoord!.x + 10,
|
|
468
|
-
},
|
|
469
|
-
}
|
|
470
|
-
const lrGraph = {
|
|
471
|
-
...graph,
|
|
472
|
-
config: { ...graph.config, graphDirection: 'LR' as const },
|
|
473
|
-
}
|
|
474
|
-
const b = {
|
|
475
|
-
...bundle,
|
|
476
|
-
sharedNode: shiftedNode,
|
|
477
|
-
sharedPath: [
|
|
478
|
-
{ x: 1, y: 2 },
|
|
479
|
-
{ x: 1, y: 4 },
|
|
480
|
-
],
|
|
481
|
-
}
|
|
482
|
-
const canvas = drawBundleArrowhead(lrGraph, b)
|
|
483
|
-
expect(nonSpaceChars(canvas)).toBe('▼')
|
|
484
|
-
})
|
|
485
|
-
})
|
|
486
|
-
|
|
487
|
-
describe('drawBundleArrowheadStart', () => {
|
|
488
|
-
const graph = buildGraph(BIDIRECTIONAL_FAN_OUT)
|
|
489
|
-
const bundle = graph.bundles[0]!
|
|
490
|
-
|
|
491
|
-
it('returns an unmodified canvas when sharedPath has fewer than 2 points', () => {
|
|
492
|
-
const shortBundle = {
|
|
493
|
-
...bundle,
|
|
494
|
-
sharedPath: [bundle.junctionPoint!],
|
|
495
|
-
}
|
|
496
|
-
const canvas = drawBundleArrowheadStart(graph, shortBundle)
|
|
497
|
-
expect(nonSpaceChars(canvas)).toBe('')
|
|
498
|
-
})
|
|
499
|
-
|
|
500
|
-
it.each<['Up' | 'Left' | 'Right', string]>([
|
|
501
|
-
['Up', '▼'],
|
|
502
|
-
['Left', '►'],
|
|
503
|
-
['Right', '◄'],
|
|
504
|
-
])(
|
|
505
|
-
'draws a trunk departing %s as the reversed %s arrowhead (unicode)',
|
|
506
|
-
(dir, expectedChar) => {
|
|
507
|
-
const b = { ...bundle, sharedPath: arrivalSharedPath(dir) }
|
|
508
|
-
const canvas = drawBundleArrowheadStart(graph, b)
|
|
509
|
-
expect(nonSpaceChars(canvas)).toBe(expectedChar)
|
|
510
|
-
},
|
|
511
|
-
)
|
|
512
|
-
|
|
513
|
-
it.each<['Up' | 'Left' | 'Right', string]>([
|
|
514
|
-
['Up', 'v'],
|
|
515
|
-
['Left', '>'],
|
|
516
|
-
['Right', '<'],
|
|
517
|
-
])(
|
|
518
|
-
'draws a trunk departing %s as the reversed %s arrowhead (ASCII)',
|
|
519
|
-
(dir, expectedChar) => {
|
|
520
|
-
const asciiGraph = buildGraph(BIDIRECTIONAL_FAN_OUT, true)
|
|
521
|
-
const asciiBundle = asciiGraph.bundles[0]!
|
|
522
|
-
const b = {
|
|
523
|
-
...asciiBundle,
|
|
524
|
-
sharedPath: arrivalSharedPath(dir),
|
|
525
|
-
}
|
|
526
|
-
const canvas = drawBundleArrowheadStart(asciiGraph, b)
|
|
527
|
-
expect(nonSpaceChars(canvas)).toBe(expectedChar)
|
|
528
|
-
},
|
|
529
|
-
)
|
|
530
|
-
|
|
531
|
-
it('falls back to the default arrowhead for a diagonal trunk direction (unicode)', () => {
|
|
532
|
-
const b = {
|
|
533
|
-
...bundle,
|
|
534
|
-
sharedPath: [
|
|
535
|
-
{ x: 1, y: 2 },
|
|
536
|
-
{ x: 5, y: 4 },
|
|
537
|
-
],
|
|
538
|
-
}
|
|
539
|
-
const canvas = drawBundleArrowheadStart(graph, b)
|
|
540
|
-
expect(nonSpaceChars(canvas)).toBe('▲')
|
|
541
|
-
})
|
|
542
|
-
|
|
543
|
-
it('falls back to the default arrowhead for a diagonal trunk direction (ASCII)', () => {
|
|
544
|
-
const asciiGraph = buildGraph(BIDIRECTIONAL_FAN_OUT, true)
|
|
545
|
-
const asciiBundle = asciiGraph.bundles[0]!
|
|
546
|
-
const b = {
|
|
547
|
-
...asciiBundle,
|
|
548
|
-
sharedPath: [
|
|
549
|
-
{ x: 1, y: 2 },
|
|
550
|
-
{ x: 5, y: 4 },
|
|
551
|
-
],
|
|
552
|
-
}
|
|
553
|
-
const canvas = drawBundleArrowheadStart(asciiGraph, b)
|
|
554
|
-
expect(nonSpaceChars(canvas)).toBe('^')
|
|
555
|
-
})
|
|
556
|
-
|
|
557
|
-
it('offsets the arrowhead horizontally for an LR graph direction', () => {
|
|
558
|
-
// Bundling never fires for LR graphs (see edge-bundling.ts), so this
|
|
559
|
-
// exercises the function's LR branch directly rather than through a
|
|
560
|
-
// real diagram.
|
|
561
|
-
const lrGraph = {
|
|
562
|
-
...graph,
|
|
563
|
-
config: { ...graph.config, graphDirection: 'LR' as const },
|
|
564
|
-
}
|
|
565
|
-
const b = {
|
|
566
|
-
...bundle,
|
|
567
|
-
sharedPath: [
|
|
568
|
-
{ x: 1, y: 2 },
|
|
569
|
-
{ x: 1, y: 4 },
|
|
570
|
-
],
|
|
571
|
-
}
|
|
572
|
-
const canvas = drawBundleArrowheadStart(lrGraph, b)
|
|
573
|
-
expect(nonSpaceChars(canvas)).toBe('▲')
|
|
574
|
-
})
|
|
575
|
-
})
|
|
576
|
-
|
|
577
|
-
describe('drawBundledEdgeArrowheadStart', () => {
|
|
578
|
-
const graph = buildGraph(BIDIRECTIONAL_FAN_IN)
|
|
579
|
-
const bundle = graph.bundles[0]!
|
|
580
|
-
const edgeTemplate = bundle.edges[0]!
|
|
581
|
-
|
|
582
|
-
it('returns an unmodified canvas when pathToJunction is undefined', () => {
|
|
583
|
-
const edge = { ...edgeTemplate, pathToJunction: undefined }
|
|
584
|
-
const canvas = drawBundledEdgeArrowheadStart(graph, edge)
|
|
585
|
-
expect(nonSpaceChars(canvas)).toBe('')
|
|
586
|
-
})
|
|
587
|
-
|
|
588
|
-
it.each<['Up' | 'Left' | 'Right', string]>([
|
|
589
|
-
['Up', '▼'],
|
|
590
|
-
['Left', '►'],
|
|
591
|
-
['Right', '◄'],
|
|
592
|
-
])(
|
|
593
|
-
'draws an edge departing %s as the reversed %s arrowhead (unicode)',
|
|
594
|
-
(dir, expectedChar) => {
|
|
595
|
-
const edge = {
|
|
596
|
-
...edgeTemplate,
|
|
597
|
-
pathToJunction: arrivalSharedPath(dir),
|
|
598
|
-
}
|
|
599
|
-
const canvas = drawBundledEdgeArrowheadStart(graph, edge)
|
|
600
|
-
expect(nonSpaceChars(canvas)).toBe(expectedChar)
|
|
601
|
-
},
|
|
602
|
-
)
|
|
603
|
-
|
|
604
|
-
it.each<['Up' | 'Left' | 'Right', string]>([
|
|
605
|
-
['Up', 'v'],
|
|
606
|
-
['Left', '>'],
|
|
607
|
-
['Right', '<'],
|
|
608
|
-
])(
|
|
609
|
-
'draws an edge departing %s as the reversed %s arrowhead (ASCII)',
|
|
610
|
-
(dir, expectedChar) => {
|
|
611
|
-
const asciiGraph = buildGraph(BIDIRECTIONAL_FAN_IN, true)
|
|
612
|
-
const asciiBundle = asciiGraph.bundles[0]!
|
|
613
|
-
const asciiEdgeTemplate = asciiBundle.edges[0]!
|
|
614
|
-
const edge = {
|
|
615
|
-
...asciiEdgeTemplate,
|
|
616
|
-
pathToJunction: arrivalSharedPath(dir),
|
|
617
|
-
}
|
|
618
|
-
const canvas = drawBundledEdgeArrowheadStart(asciiGraph, edge)
|
|
619
|
-
expect(nonSpaceChars(canvas)).toBe(expectedChar)
|
|
620
|
-
},
|
|
621
|
-
)
|
|
622
|
-
|
|
623
|
-
it('falls back to the default arrowhead for a diagonal edge direction (unicode)', () => {
|
|
624
|
-
const edge = {
|
|
625
|
-
...edgeTemplate,
|
|
626
|
-
pathToJunction: [
|
|
627
|
-
{ x: 1, y: 2 },
|
|
628
|
-
{ x: 5, y: 4 },
|
|
629
|
-
],
|
|
630
|
-
}
|
|
631
|
-
const canvas = drawBundledEdgeArrowheadStart(graph, edge)
|
|
632
|
-
expect(nonSpaceChars(canvas)).toBe('▼')
|
|
633
|
-
})
|
|
634
|
-
|
|
635
|
-
it('falls back to the default arrowhead for a diagonal edge direction (ASCII)', () => {
|
|
636
|
-
const asciiGraph = buildGraph(BIDIRECTIONAL_FAN_IN, true)
|
|
637
|
-
const asciiBundle = asciiGraph.bundles[0]!
|
|
638
|
-
const asciiEdgeTemplate = asciiBundle.edges[0]!
|
|
639
|
-
const edge = {
|
|
640
|
-
...asciiEdgeTemplate,
|
|
641
|
-
pathToJunction: [
|
|
642
|
-
{ x: 1, y: 2 },
|
|
643
|
-
{ x: 5, y: 4 },
|
|
644
|
-
],
|
|
645
|
-
}
|
|
646
|
-
const canvas = drawBundledEdgeArrowheadStart(asciiGraph, edge)
|
|
647
|
-
expect(nonSpaceChars(canvas)).toBe('v')
|
|
648
|
-
})
|
|
649
|
-
})
|
|
650
|
-
|
|
651
|
-
describe('drawBundledEdgeArrowhead', () => {
|
|
652
|
-
const graph = buildGraph(FAN_OUT)
|
|
653
|
-
const bundle = graph.bundles[0]!
|
|
654
|
-
const edgeTemplate = bundle.edges[0]!
|
|
655
|
-
|
|
656
|
-
it('returns an unmodified canvas when pathToJunction is undefined', () => {
|
|
657
|
-
const edge = { ...edgeTemplate, pathToJunction: undefined }
|
|
658
|
-
const canvas = drawBundledEdgeArrowhead(graph, edge)
|
|
659
|
-
expect(nonSpaceChars(canvas)).toBe('')
|
|
660
|
-
})
|
|
661
|
-
|
|
662
|
-
it.each<['Up' | 'Left' | 'Right', string]>([
|
|
663
|
-
['Up', '▲'],
|
|
664
|
-
['Left', '◄'],
|
|
665
|
-
['Right', '►'],
|
|
666
|
-
])(
|
|
667
|
-
'draws a %s-arriving edge as the %s arrowhead (unicode)',
|
|
668
|
-
(dir, expectedChar) => {
|
|
669
|
-
const edge = {
|
|
670
|
-
...edgeTemplate,
|
|
671
|
-
pathToJunction: arrivalSharedPath(dir),
|
|
672
|
-
}
|
|
673
|
-
const canvas = drawBundledEdgeArrowhead(graph, edge)
|
|
674
|
-
expect(nonSpaceChars(canvas)).toBe(expectedChar)
|
|
675
|
-
},
|
|
676
|
-
)
|
|
677
|
-
|
|
678
|
-
it.each<['Up' | 'Left' | 'Right', string]>([
|
|
679
|
-
['Up', '^'],
|
|
680
|
-
['Left', '<'],
|
|
681
|
-
['Right', '>'],
|
|
682
|
-
])(
|
|
683
|
-
'draws a %s-arriving edge as the %s arrowhead (ASCII)',
|
|
684
|
-
(dir, expectedChar) => {
|
|
685
|
-
const asciiGraph = buildGraph(FAN_OUT, true)
|
|
686
|
-
const asciiBundle = asciiGraph.bundles[0]!
|
|
687
|
-
const asciiEdgeTemplate = asciiBundle.edges[0]!
|
|
688
|
-
const edge = {
|
|
689
|
-
...asciiEdgeTemplate,
|
|
690
|
-
pathToJunction: arrivalSharedPath(dir),
|
|
691
|
-
}
|
|
692
|
-
const canvas = drawBundledEdgeArrowhead(asciiGraph, edge)
|
|
693
|
-
expect(nonSpaceChars(canvas)).toBe(expectedChar)
|
|
694
|
-
},
|
|
695
|
-
)
|
|
696
|
-
|
|
697
|
-
it('falls back to the default arrowhead for a diagonal edge direction (unicode)', () => {
|
|
698
|
-
const edge = {
|
|
699
|
-
...edgeTemplate,
|
|
700
|
-
pathToJunction: [
|
|
701
|
-
{ x: 1, y: 2 },
|
|
702
|
-
{ x: 5, y: 4 },
|
|
703
|
-
],
|
|
704
|
-
}
|
|
705
|
-
const canvas = drawBundledEdgeArrowhead(graph, edge)
|
|
706
|
-
expect(nonSpaceChars(canvas)).toBe('▼')
|
|
707
|
-
})
|
|
708
|
-
|
|
709
|
-
it('falls back to the default arrowhead for a diagonal edge direction (ASCII)', () => {
|
|
710
|
-
const asciiGraph = buildGraph(FAN_OUT, true)
|
|
711
|
-
const asciiBundle = asciiGraph.bundles[0]!
|
|
712
|
-
const asciiEdgeTemplate = asciiBundle.edges[0]!
|
|
713
|
-
const edge = {
|
|
714
|
-
...asciiEdgeTemplate,
|
|
715
|
-
pathToJunction: [
|
|
716
|
-
{ x: 1, y: 2 },
|
|
717
|
-
{ x: 5, y: 4 },
|
|
718
|
-
],
|
|
719
|
-
}
|
|
720
|
-
const canvas = drawBundledEdgeArrowhead(asciiGraph, edge)
|
|
721
|
-
expect(nonSpaceChars(canvas)).toBe('v')
|
|
722
|
-
})
|
|
723
|
-
|
|
724
|
-
it('offsets the arrowhead horizontally for an LR graph direction', () => {
|
|
725
|
-
// Same rationale as the drawBundleArrowhead LR test above: bundling
|
|
726
|
-
// never fires for LR graphs, so this directly exercises the LR branch.
|
|
727
|
-
const shiftedTo = {
|
|
728
|
-
...edgeTemplate.to,
|
|
729
|
-
drawingCoord: {
|
|
730
|
-
...edgeTemplate.to.drawingCoord!,
|
|
731
|
-
x: edgeTemplate.to.drawingCoord!.x + 10,
|
|
732
|
-
},
|
|
733
|
-
}
|
|
734
|
-
const lrGraph = {
|
|
735
|
-
...graph,
|
|
736
|
-
config: { ...graph.config, graphDirection: 'LR' as const },
|
|
737
|
-
}
|
|
738
|
-
const edge = {
|
|
739
|
-
...edgeTemplate,
|
|
740
|
-
to: shiftedTo,
|
|
741
|
-
pathToJunction: [
|
|
742
|
-
{ x: 1, y: 2 },
|
|
743
|
-
{ x: 1, y: 4 },
|
|
744
|
-
],
|
|
745
|
-
}
|
|
746
|
-
const canvas = drawBundledEdgeArrowhead(lrGraph, edge)
|
|
747
|
-
expect(nonSpaceChars(canvas)).toBe('▼')
|
|
748
|
-
})
|
|
749
|
-
})
|
|
750
|
-
|
|
751
|
-
describe('drawJunctionCharacter', () => {
|
|
752
|
-
const graph = buildGraph(FAN_IN)
|
|
753
|
-
const bundle = graph.bundles[0]!
|
|
754
|
-
const junction = bundle.junctionPoint!
|
|
755
|
-
|
|
756
|
-
type Dir = 'Down' | 'Up' | 'Left' | 'Right'
|
|
757
|
-
|
|
758
|
-
/** A point that arrives at the junction moving in the given direction. */
|
|
759
|
-
function arrivalPoint(dir: Dir): GridCoord {
|
|
760
|
-
switch (dir) {
|
|
761
|
-
case 'Down':
|
|
762
|
-
return { x: junction.x, y: junction.y - 1 }
|
|
763
|
-
case 'Up':
|
|
764
|
-
return { x: junction.x, y: junction.y + 1 }
|
|
765
|
-
case 'Right':
|
|
766
|
-
return { x: junction.x - 1, y: junction.y }
|
|
767
|
-
case 'Left':
|
|
768
|
-
return { x: junction.x + 1, y: junction.y }
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
/** A point the shared trunk continues to from the junction, in the given direction. */
|
|
773
|
-
function sharedNextPoint(dir: Dir): GridCoord {
|
|
774
|
-
switch (dir) {
|
|
775
|
-
case 'Down':
|
|
776
|
-
return { x: junction.x, y: junction.y + 1 }
|
|
777
|
-
case 'Up':
|
|
778
|
-
return { x: junction.x, y: junction.y - 1 }
|
|
779
|
-
case 'Right':
|
|
780
|
-
return { x: junction.x + 1, y: junction.y }
|
|
781
|
-
case 'Left':
|
|
782
|
-
return { x: junction.x - 1, y: junction.y }
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
function makeBundle(sharedDir: Dir | null, arrivalDirs: Dir[]): EdgeBundle {
|
|
787
|
-
const sharedPath = sharedDir ? [junction, sharedNextPoint(sharedDir)] : []
|
|
788
|
-
const edges = arrivalDirs.map((d) => ({
|
|
789
|
-
...bundle.edges[0]!,
|
|
790
|
-
pathToJunction: [arrivalPoint(d), junction],
|
|
791
|
-
}))
|
|
792
|
-
return { ...bundle, sharedPath, edges }
|
|
793
|
-
}
|
|
794
|
-
|
|
795
|
-
it.each<[string, Dir | null, Dir[], string]>([
|
|
796
|
-
['all four directions', 'Down', ['Down', 'Right', 'Left'], '┼'],
|
|
797
|
-
['down + left + right (no up)', 'Down', ['Right', 'Left'], '┬'],
|
|
798
|
-
['up + left + right (no down)', 'Up', ['Right', 'Left'], '┴'],
|
|
799
|
-
['up + down + left (no right)', 'Down', ['Down', 'Right'], '┤'],
|
|
800
|
-
['left + right only', null, ['Right', 'Left'], '─'],
|
|
801
|
-
['up + down only', 'Down', ['Down'], '│'],
|
|
802
|
-
['down + left only', 'Down', ['Right'], '┐'],
|
|
803
|
-
['up + right only', 'Up', ['Left'], '└'],
|
|
804
|
-
['up + left only', 'Up', ['Right'], '┘'],
|
|
805
|
-
['a single direction (no named shape)', 'Up', [], '┼'],
|
|
806
|
-
['no connecting directions at all', null, [], '┼'],
|
|
807
|
-
[
|
|
808
|
-
'shared trunk exits right, edge arrives from above',
|
|
809
|
-
'Right',
|
|
810
|
-
['Down'],
|
|
811
|
-
'└',
|
|
812
|
-
],
|
|
813
|
-
['shared trunk exits left, edge arrives from above', 'Left', ['Down'], '┘'],
|
|
814
|
-
])('draws %s', (_label, sharedDir, arrivalDirs, expectedChar) => {
|
|
815
|
-
const b = makeBundle(sharedDir, arrivalDirs)
|
|
816
|
-
const canvas = drawJunctionCharacter(graph, b)
|
|
817
|
-
expect(nonSpaceChars(canvas)).toBe(expectedChar)
|
|
818
|
-
})
|
|
819
|
-
|
|
820
|
-
it('draws "+" for any connecting directions in ASCII charset', () => {
|
|
821
|
-
const asciiGraph = buildGraph(FAN_IN, true)
|
|
822
|
-
const asciiBundle = asciiGraph.bundles[0]!
|
|
823
|
-
const asciiJunction = asciiBundle.junctionPoint!
|
|
824
|
-
const b = {
|
|
825
|
-
...asciiBundle,
|
|
826
|
-
sharedPath: [
|
|
827
|
-
asciiJunction,
|
|
828
|
-
{ x: asciiJunction.x, y: asciiJunction.y + 1 },
|
|
829
|
-
],
|
|
830
|
-
edges: [
|
|
831
|
-
{
|
|
832
|
-
...asciiBundle.edges[0]!,
|
|
833
|
-
pathToJunction: [
|
|
834
|
-
{ x: asciiJunction.x - 1, y: asciiJunction.y },
|
|
835
|
-
asciiJunction,
|
|
836
|
-
],
|
|
837
|
-
},
|
|
838
|
-
],
|
|
839
|
-
}
|
|
840
|
-
const canvas = drawJunctionCharacter(asciiGraph, b)
|
|
841
|
-
expect(nonSpaceChars(canvas)).toBe('+')
|
|
842
|
-
})
|
|
843
|
-
|
|
844
|
-
it('returns an unmodified canvas when junctionPoint is null', () => {
|
|
845
|
-
const b = { ...bundle, junctionPoint: null }
|
|
846
|
-
const canvas = drawJunctionCharacter(graph, b)
|
|
847
|
-
expect(nonSpaceChars(canvas)).toBe('')
|
|
848
|
-
})
|
|
849
|
-
})
|