u
This commit is contained in:
@@ -711,6 +711,61 @@ function countSvgCrossings(svg) {
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Graphviz self-edges from a lower FK row to id:e dip southeast. Rebuild the
|
||||
* spline so it bulges east + north (up) while keeping start/end ports.
|
||||
* In this SVG, more-negative y is visually higher.
|
||||
*/
|
||||
function fixSelfLoopSvg(svg) {
|
||||
return svg.replace(
|
||||
/<g id="(edge\d+)" class="edge">\s*<title>(\w+):e->\2:e<\/title>\s*<g id="(a_edge\d+)"><a ([^>]+)>\s*<path([^>]*) d="([^"]+)"\/>\s*<polygon([^>]*) points="([^"]+)"\/>\s*<\/a>\s*<\/g>\s*<\/g>/g,
|
||||
(match, edgeId, _table, aId, aAttrs, pathAttrs, d, polyAttrs) => {
|
||||
const pts = [...d.matchAll(/([-\d.]+)[,\s]+([-\d.]+)/g)].map((m) => [
|
||||
Number(m[1]),
|
||||
Number(m[2]),
|
||||
]);
|
||||
if (pts.length < 2) return match;
|
||||
|
||||
const [x0, y0] = pts[0];
|
||||
const [x1, y1] = pts[pts.length - 1];
|
||||
const east = Math.max(x0, x1) + 36;
|
||||
const north = Math.min(y0, y1) - 28;
|
||||
|
||||
// Cubic end tangent is P3-P2; orient arrow along that arrival direction.
|
||||
const tx = x1 - east;
|
||||
const ty = y1 - north;
|
||||
const len = Math.hypot(tx, ty) || 1;
|
||||
const dx = tx / len;
|
||||
const dy = ty / len;
|
||||
const arrowLen = 9;
|
||||
const arrowHalf = 3.5;
|
||||
const baseX = x1 - dx * arrowLen;
|
||||
const baseY = y1 - dy * arrowLen;
|
||||
const px = -dy * arrowHalf;
|
||||
const py = dx * arrowHalf;
|
||||
|
||||
const fmt = (n) => (Math.round(n * 100) / 100).toString();
|
||||
const newDFmt = `M${fmt(x0)},${fmt(y0)}C${fmt(east)},${fmt(y0)} ${fmt(east)},${fmt(north)} ${fmt(baseX)},${fmt(baseY)}`;
|
||||
const newPointsFmt =
|
||||
`${fmt(x1)},${fmt(y1)} ` +
|
||||
`${fmt(baseX + px)},${fmt(baseY + py)} ` +
|
||||
`${fmt(baseX - px)},${fmt(baseY - py)} ` +
|
||||
`${fmt(x1)},${fmt(y1)}`;
|
||||
|
||||
return (
|
||||
`<g id="${edgeId}" class="edge">\n` +
|
||||
`<title>${_table}:e->${_table}:e</title>\n` +
|
||||
`<g id="${aId}"><a ${aAttrs}>\n` +
|
||||
`<path${pathAttrs} d="${newDFmt}"/>\n` +
|
||||
`<polygon${polyAttrs} points="${newPointsFmt}"/>\n` +
|
||||
`</a>\n` +
|
||||
`</g>\n` +
|
||||
`</g>`
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const schemaSource = readFileSync(schemaPath, 'utf8');
|
||||
const { tables, edges } = parseSchema(schemaSource);
|
||||
|
||||
@@ -742,7 +797,7 @@ for (let pass = 0; pass < 2; pass++) {
|
||||
const dot = buildDot(tables, edges, positions);
|
||||
writeFileSync(dotOutputPath, dot, 'utf8');
|
||||
|
||||
const svg = graphviz.layout(dot, 'svg', 'dot');
|
||||
const svg = fixSelfLoopSvg(graphviz.layout(dot, 'svg', 'dot'));
|
||||
writeFileSync(outputPath, svg, 'utf8');
|
||||
|
||||
const dim = svg.match(/width="([\d.]+)pt" height="([\d.]+)pt"/);
|
||||
|
||||
Reference in New Issue
Block a user