import type { Group } from '../shapes/Group'; import type { Node } from '../Node'; import type { Obstacle } from './types'; /** Convert world coordinates to a parent group's local space */ export declare function worldToParentLocal(parent: Group, wx: number, wy: number): { x: number; y: number; }; /** Convert obstacle bbox from world space to parent local space */ export declare function obstacleToParentLocal(parent: Group, obs: Obstacle): Obstacle; /** * Card size from metadata — ignores nested child groups (org/mind leaves). * Critical for correct connector anchors on nested diagram trees. */ export declare function getDiagramCardSize(node: Node): { width: number; height: number; } | null; /** * Accumulate node.x/y from `node` up to (but not including) `parent`. * Uses live position values — correct during drag even if world matrices are stale. */ export declare function positionInParent(node: Node, parent: Group): { x: number; y: number; }; /** * Map a point in the node's unscaled local card space into parent-local coords, * applying the node's scale then rotation (same order as Node.getLocalMatrix). */ export declare function nodeLocalToParent(node: Node, parent: Group, lx: number, ly: number): { x: number; y: number; }; /** Local content box of a node (card metadata or children AABB), ignoring nested diagram nodes. */ export declare function getLocalNodeBox(node: Node): { x: number; y: number; width: number; height: number; }; /** Node content box in a parent group's local coordinates (drag-safe, rotation-aware AABB). */ export declare function getNodeBoxInParent(node: Node, parent: Group): { x: number; y: number; width: number; height: number; }; /** Transform a point in node-local space into a parent group's local space */ export declare function localPointToParent(node: Node, parent: Group, lx: number, ly: number): { x: number; y: number; }; export type CardSide = 'top' | 'bottom' | 'left' | 'right' | 'center'; /** * Connection port on a node edge, in parent local space — respects rotation. * @param along 0..1 position along the side (0.5 = mid). Top/bottom: left→right; * left/right: top→bottom. Ignored for `center`. */ export declare function getCardSideAnchor(node: Node, parent: Group, side: CardSide, along?: number): { x: number; y: number; }; /** * Pick the best pair of connection ports so the wire attaches to node edges * and stretches as nodes move (draw.io-style). */ export declare function pickConnectionSides(fromBox: { x: number; y: number; width: number; height: number; }, toBox: { x: number; y: number; width: number; height: number; }): { fromSide: CardSide; toSide: CardSide; }; /** Card-only obstacle box in parent-local space (excludes nested children). */ export declare function getCardObstacleInParent(node: Node, parent: Group): Obstacle | null; /** Card-only obstacle box in world space (excludes nested children). */ export declare function getCardObstacle(node: Node): Obstacle | null; /** * Anchor points between two nodes in parent group's local coordinates. * Chooses facing mid-side ports on the rotated cards so wires attach cleanly * after move/resize/rotate; users can still add manual bend points later. * * Optional `fromAlong` / `toAlong` (0..1) fan multiple wires off the same side * so decision branches and parallel connectors do not stack on one point. */ export declare function getConnectorAnchors(from: Node, to: Node, parent: Group, opts?: { fromAlong?: number; toAlong?: number; }): { x1: number; y1: number; x2: number; y2: number; fromSide: CardSide; toSide: CardSide; }; /** Evenly spaced `along` values in (0.2, 0.8) so stacked ports stay on the card edge. */ export declare function fanAlongSlots(count: number): number[]; export interface EdgeFanPlan { fromAlong: number; toAlong: number; fromSide: CardSide; toSide: CardSide; x1: number; y1: number; x2: number; y2: number; /** Parallel-rail offset (px) so stacked orthogonal paths do not coincide. */ railOffset: number; } /** * Assign connection ports so wires use free sides of each symbol when possible. * * Rules: * 1. Each edge prefers the side facing its peer (left target → left port, etc.). * 2. Edges that share the same side fan along that side (distinct along slots). * 3. Parallel routes get a railOffset so mid-segments do not paint on top of each other. * * Does not force every outbound edge onto one side — free sides stay available. */ export declare function planEdgeFanAnchors(edges: Array<{ key: string; from: Node; to: Node; }>, parent: Group): Map; export interface PortCandidate { side: CardSide; along: number; x: number; y: number; } /** Sample ports around a card border (4 sides × several along slots). */ export declare function enumerateCardPorts(node: Node, parent: Group, alongSamples?: number[]): PortCandidate[]; /** * True when the first route stub from an attachment point goes into the card * interior (arrow/edge overrides the border) instead of leaving cleanly. */ export declare function attachmentOverridesBorder(attachX: number, attachY: number, peerX: number, peerY: number, box: { x: number; y: number; width: number; height: number; }, side: CardSide): boolean; /** * Pick nearest free port on the same symbol that faces the peer and is not * already taken by another edge endpoint. */ export declare function nearestFreePortOnSymbol(node: Node, parent: Group, peerX: number, peerY: number, occupied: Array<{ x: number; y: number; }>, preferSide?: CardSide): PortCandidate | null; /** * Prefer the card side facing the peer. Uses a lower lateral threshold so * left/right ports are chosen when free instead of stacking everything on bottom/top. */ export declare function preferredExitSide(dx: number, dy: number): CardSide; /** @deprecated use preferredExitSide — kept for call-site clarity in older notes */ export declare function dominantSideFromDelta(dx: number, dy: number): CardSide; /** Legacy world-space anchors (when no parent group is provided). */ export declare function getConnectorAnchorsWorld(from: Node, to: Node): { x1: number; y1: number; x2: number; y2: number; };