mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-09-01 17:10:24 +08:00
feat(diagram-engine): label avoidance, paired opposite edges, semantic group colours
A git-workflow flowchart rendered with no overlaps but read poorly. Three distinct causes, each fixed and measured: 1. Edge labels sat on boxes and on each other (4 collisions on the reported diagram; 280 across 250 generated flowcharts). The router keeps LINES off the boxes but a label renders at its edge's midpoint, which on a long edge is beside exactly the things the line was routed around. placeLabels slides each label along its own edge to a clear spot — longest edges first, midpoint-outward tries — written as the geometry's relative x, which draw.io natively supports. Corpus: 280 label collisions -> 8. 2. A->B and B->A were routed independently, so "git add" ran straight while "git reset" wandered through a different corridor with a kink. Opposite edges that agree on axis now get two absolute parallel tracks in the strip where the two boxes overlap, a constant 24px apart, converted back to port fractions. Zero crossing regressions. 3. All boxes rendered the same white, because the render layer's fill/stroke support was never reachable: neither add_box's schema nor draw_graph's nodes exposed it. Rather than exposing raw hex (the model picks mismatched saturations, differently every time), nodes take a semantic group name and the engine maps groups to a fixed palette of six paired fill/strokes in order of first appearance. The model names the zones - remote vs local vs temp - and never touches a colour. 532 unit tests pass; the 5 diagram e2e tests pass in a real browser.
This commit is contained in:
@@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
import type { Operation } from "./operations"
|
||||
import { groupColour } from "./render"
|
||||
import type { BoxShape } from "./types"
|
||||
|
||||
/** A node in the graph the caller wants drawn. */
|
||||
@@ -33,6 +34,12 @@ export interface GraphNode {
|
||||
shape?: BoxShape
|
||||
/** Catalog stencil name. When set the node renders as an icon rather than a box. */
|
||||
icon?: string
|
||||
/**
|
||||
* Semantic group name, e.g. "remote" or "local". Nodes sharing a group get the same
|
||||
* fill colour from the engine's palette, assigned in order of first appearance — the
|
||||
* caller names the grouping and never touches a colour.
|
||||
*/
|
||||
group?: string
|
||||
}
|
||||
|
||||
/** An arrow. Direction matters: it is what determines the layering. */
|
||||
@@ -302,8 +309,19 @@ export function graphToOperations(
|
||||
},
|
||||
]
|
||||
const byId = new Map(nodes.map((n) => [n.id, n]))
|
||||
// Groups become colours here, in order of first appearance, so "the second group named
|
||||
// is green" holds for every diagram the engine draws. The caller only names groups.
|
||||
const groupIndex = new Map<string, number>()
|
||||
for (const n of nodes)
|
||||
if (n.group && !groupIndex.has(n.group))
|
||||
groupIndex.set(n.group, groupIndex.size)
|
||||
|
||||
const add = (id: string, parent: string): Operation => {
|
||||
const n = byId.get(id) as GraphNode
|
||||
const colour =
|
||||
n.group !== undefined
|
||||
? groupColour(groupIndex.get(n.group) ?? 0)
|
||||
: null
|
||||
return n.icon
|
||||
? {
|
||||
op: "add_icon",
|
||||
@@ -318,6 +336,9 @@ export function graphToOperations(
|
||||
parent,
|
||||
label: n.label,
|
||||
...(n.shape && n.shape !== "box" ? { shape: n.shape } : {}),
|
||||
...(colour
|
||||
? { fill: colour.fill, stroke: colour.stroke }
|
||||
: {}),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user