Restructure to skill-manager conventions
- Move the TypeScript project under scripts/ (src, tsconfig, package.json, pnpm lockfile/workspace, canonical .gitignore); drop the npm package-lock - Add scripts/Taskfile.yml aggregator plus .scripts modules (loggers, base, cli) with build, run, and validate tasks - Move the six SKILL-*.md docs into references/ with kebab names and extract the connector-routing sections from SKILL.md into references/routing-best-practices.md (SKILL.md 666 -> ~310 lines) - Add license/metadata/compatibility frontmatter, an Available scripts section, and update all CLI paths in README and references skill-manager validate: 13/13 passed, 0 warnings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* connector-router — computes the actual routed path for draw.io connectors.
|
||||
*
|
||||
* Strategy: load the diagram XML into a real maxGraph Graph+GraphView instance
|
||||
* via graph-loader, which calls GraphView.validate() to compute all cell states
|
||||
* exactly as draw.io does at render time. Edge absolutePoints are taken directly
|
||||
* from those states — no synthetic geometry approximation.
|
||||
*
|
||||
* This is the only correct approach: draw.io's routing algorithms (especially
|
||||
* OrthConnector for edgeStyle=orthogonalEdgeStyle) depend on the full graph
|
||||
* state including parent container bounds, so they cannot be reproduced
|
||||
* accurately without loading the full model.
|
||||
*/
|
||||
|
||||
import { loadGraphStates } from "../maxgraph-loader/graph-loader.js";
|
||||
import type { Edge } from "../drawio-parser/parser.js";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public API
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export interface RoutedPoint {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a map of edgeId → routed points for ALL edges in the diagram.
|
||||
*
|
||||
* Call this once per diagram page and reuse the result for all edges.
|
||||
* Using `routeAllEdges` is significantly more efficient than calling
|
||||
* `routeEdge` in a loop because it runs `GraphView.validate()` only once.
|
||||
*
|
||||
* @param graphModelXml - Raw <mxGraphModel> XML for the page
|
||||
* @returns Map of edge id → array of canvas-absolute routed points
|
||||
*/
|
||||
export function routeAllEdges(graphModelXml: string): Map<string, RoutedPoint[]> {
|
||||
const { edgeRoutes } = loadGraphStates(graphModelXml);
|
||||
const result = new Map<string, RoutedPoint[]>();
|
||||
for (const [id, route] of edgeRoutes) {
|
||||
result.set(id, route.points);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get routed points for a single edge from a pre-computed route map.
|
||||
*
|
||||
* For efficiency, pre-compute the map once using `routeAllEdges` and pass it here.
|
||||
* Falls back to a straight line between shape centers if the edge is not found.
|
||||
*
|
||||
* @param edge - The edge to route
|
||||
* @param routeMap - Pre-computed map from `routeAllEdges`
|
||||
* @param shapeMidpoints - Optional fallback: map of shapeId → {midX, midY}
|
||||
*/
|
||||
export function getEdgeRoute(
|
||||
edge: Edge,
|
||||
routeMap: Map<string, RoutedPoint[]>,
|
||||
shapeMidpoints?: Map<string, { midX: number; midY: number }>
|
||||
): RoutedPoint[] {
|
||||
const pts = routeMap.get(edge.id);
|
||||
if (pts && pts.length >= 2) return pts;
|
||||
|
||||
// Fallback: straight line using shape midpoints
|
||||
if (shapeMidpoints && edge.sourceId && edge.targetId) {
|
||||
const src = shapeMidpoints.get(edge.sourceId);
|
||||
const tgt = shapeMidpoints.get(edge.targetId);
|
||||
if (src && tgt) {
|
||||
return [{ x: src.midX, y: src.midY }, { x: tgt.midX, y: tgt.midY }];
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
Reference in New Issue
Block a user