/** * 拓扑连线:锚点坐标、自动选边、正交折线路径 */ import type { FloorMapEdge, FloorMapEdgeAnchor, FloorMapEdgeRouting, FloorMapItemBase } from '../types'; export interface FloorMapItemPixelRect { left: number; top: number; width: number; height: number; } export interface FloorMapEdgeRoute { edgeId: string; points: [number, number][]; pathD: string; } declare const ANCHORS: FloorMapEdgeAnchor[]; /** * 将图元逻辑坐标转为内容区像素矩形(与 EditableItemLayer 一致) */ export declare function getItemPixelRect(item: T, options: { cellSize: number; itemUnit: 'cell' | 'pixel'; itemGap?: number; override?: Partial; }): FloorMapItemPixelRect; export declare function rectCenter(rect: FloorMapItemPixelRect): { x: number; y: number; }; /** * 锚点在矩形边框上的坐标 */ export declare function getAnchorPoint(rect: FloorMapItemPixelRect, anchor: FloorMapEdgeAnchor): { x: number; y: number; }; /** 与 EdgeLayer 端口标签 pill 高度一致 */ export declare const EDGE_PORT_LABEL_HEIGHT = 18; /** 标签中心相对边框锚点的外移间距(px) */ export declare const EDGE_PORT_LABEL_OFFSET_GAP = 6; /** * 编辑态连线锚点圆点外移距离(px),避开 EditableItemLayer 边中点 20×20 缩放手柄。 */ export declare const EDGE_PORT_MARKER_OUTSET = 16; /** 折线绕开图元矩形的间距(px) */ export declare const EDGE_ROUTE_RECT_CLEARANCE = 10; /** * 编辑态锚点圆点 UI 位置(相对图元局部坐标),沿外法线外移,连线几何仍用 {@link getAnchorPoint}。 */ export declare function getPortMarkerDisplayPoint(rect: FloorMapItemPixelRect, anchor: FloorMapEdgeAnchor, outset?: number): { x: number; y: number; }; export declare function getPortLabelPosition(rect: FloorMapItemPixelRect, anchor: FloorMapEdgeAnchor, options?: { labelHeight?: number; gap?: number; }): { x: number; y: number; }; /** 折线中段连接类型标签相对路径的默认外偏移(px) */ export declare const EDGE_CENTER_LABEL_OFFSET = 8; export declare type EdgeCenterLabelPlacement = 'middle' | 'target-tail' | 'source-tail' | 'longest-horizontal'; /** 与 EdgeLayer PortLabel 估算宽度一致 */ export declare function estimateEdgeLabelWidth(text: string): number; /** * 在折线指定段上计算标签中心(水平段:标签在线上方;竖直段:标签在线左侧)。 */ export declare function getEdgeCenterLabelPositionOnSegment(points: [number, number][], segIndex: number, options?: { along?: number; offsetPx?: number; perpOffsetPx?: number; }): { x: number; y: number; } | null; /** * 计算边 `label`(如 connection_type)在正交折线上的显示位置。 * 默认 `target-tail`:靠近路径末端(目标端),避免标在水平总线中段互相重叠。 */ export declare function getEdgeCenterLabelPosition(points: [number, number][], options?: { placement?: EdgeCenterLabelPlacement; offsetPx?: number; /** 0–1,沿标签所在段的位置 */ along?: number; /** 垂直于段的额外偏移(水平段:y 方向;竖直段:x 方向,正数向左) */ perpOffsetPx?: number; /** 指定段索引;缺省时由 placement 推导 */ segIndex?: number; }): { x: number; y: number; } | null; /** 标签防碰撞:中心点最小间距(px,约等于 pill 高度 + 间距) */ export declare const EDGE_CENTER_LABEL_MIN_GAP = 22; export interface ResolveEdgeCenterLabelItem { edgeId: string; sourceId: string; targetId: string; points: [number, number][]; label?: string; } /** * 为多条边的连接类型标签分配互不重叠的位置。 * - 多边汇入同一目标:优先标在各自「水平总线」段中点(天然错开) * - 仍重叠时按 pill 宽高做 bbox 分离;必要时在目标图元左侧纵向堆叠 */ export declare function resolveEdgeCenterLabelPositions(items: ResolveEdgeCenterLabelItem[], options?: { placement?: EdgeCenterLabelPlacement; offsetPx?: number; rectsById?: Map; }): Map; /** * 未指定锚点时,选朝向对方最近的一侧 */ export declare function pickAutoAnchor(from: FloorMapItemPixelRect, to: FloorMapItemPixelRect): FloorMapEdgeAnchor; /** 拉线吸附端口时的命中半径(与 {@link EDGE_PORT_MARKER_OUTSET} 同量级) */ export declare const EDGE_PORT_SNAP_THRESHOLD = 16; export interface DrawingTargetHit { instanceId: string; anchor: FloorMapEdgeAnchor; } /** * 距矩形四边锚点最近的一侧 */ export declare function pickNearestAnchorOnRect(pt: { x: number; y: number; }, rect: FloorMapItemPixelRect): FloorMapEdgeAnchor; /** * 拉线过程中解析目标:优先吸附到端口圆点,否则落在图元内时取最近端口。 */ export declare function resolveDrawingTargetAtPoint(pt: { x: number; y: number; }, instanceIds: string[], rectsById: Map, excludeInstanceId: string, snapThreshold?: number): DrawingTargetHit | null; /** 折线是否有线段穿过矩形内部 */ export declare function pathCrossesRectInterior(points: [number, number][], rect: FloorMapItemPixelRect): boolean; export interface RouteOrthogonalOptions { /** 需要绕开的图元矩形(通常为对端节点) */ obstacleRects?: FloorMapItemPixelRect[]; clearance?: number; /** @deprecated 使用 obstacleRects;保留兼容,等价于 [targetRect] */ sourceRect?: FloorMapItemPixelRect; targetRect?: FloorMapItemPixelRect; /** * 通道偏移(px,带符号):用于让多条共享同一通道的连线错开,避免视觉重叠。 * 影响 Z 型中点(midX/midY)与外侧公共通道(above/below/left/right)。 */ channelOffset?: number; } /** * 正交连线:先最短路径,再对穿框段绕一圈。 */ export declare function routeOrthogonal(sourcePt: { x: number; y: number; }, sourceSide: FloorMapEdgeAnchor, targetPt: { x: number; y: number; }, targetSide: FloorMapEdgeAnchor, /** @deprecated 已取消固定 stub;可传 number(忽略)或 RouteOrthogonalOptions */ stubOrOptions?: number | RouteOrthogonalOptions, maybeOptions?: RouteOrthogonalOptions): [number, number][]; /** * 折线转带圆角的 SVG path d */ export declare function pathFromPoints(points: [number, number][], radius?: number): string; export interface BuildEdgeRouteOptions { /** 共享通道时的横向/纵向错位偏移 */ channelOffset?: number; /** 用户手动调整后的路由覆盖;优先于 edge.routing 字段 */ routingOverride?: FloorMapEdgeRouting; } /** * 把一条已存在的 path 快照(snapshot)的两端「平移对齐」到新的源/目标锚点位置, * 中间路径完全保留。用于「手动编辑过的连线」在图元被拖动后只更新最近两端。 * * 规则: * - 首段(snapshot[0] → snapshot[1]): * - 若是水平段(y0 === y1):新 snapshot[0] = srcPt;新 snapshot[1] = (原 snapshot[1].x, srcPt.y) * - 若是竖直段(x0 === x1):新 snapshot[0] = srcPt;新 snapshot[1] = (srcPt.x, 原 snapshot[1].y) * - 若是斜段(理论上 snapshot 都是正交,但兜底):仅平移 snapshot[0],snapshot[1] 保持不变 * - 末段(snapshot[n-2] → snapshot[n-1]):对称处理 * - 同时对 snapshot[2] / snapshot[n-3] 做"必要的轴向跟随":snapshot[1] → snapshot[2] 段必须保持 * 正交,因此 snapshot[2] 的相邻轴坐标需要跟随 snapshot[1] 更新(同理末端)。 */ export declare function alignSnapshotToAnchors(snapshot: [number, number][], srcPt: { x: number; y: number; }, tgtPt: { x: number; y: number; }): [number, number][]; /** * 将「中段坐标覆盖」应用到一条正交折线 path 上。 * * 内部段(既非首段也非末段)按顺序对应 `midSegmentPositions[k]`。 * - 水平段(y1 === y2)→ 把 result[i].y / result[i+1].y 都改成 v * - 竖直段(x1 === x2)→ 把 result[i].x / result[i+1].x 都改成 v * * 移动一个内部段只是平移它的主轴坐标,相邻段(必是另一个方向)会随之延长/缩短, * 不会破坏正交性。多余的 override 项被忽略,null/undefined 项跳过。 */ /** 首/末段拖动后默认插入的 stub 长度(px);过短时按段长 50% 自动缩小 */ export declare const DEFAULT_EDGE_STUB_LENGTH = 24; /** * 在路径起点处插入 stub 转角:把首段变成「沿首段实际方向走 stub → 90° 转弯 → * 走到原 corner」。 * * 注意:方向取自 *path 首段实际指向*(points[0]→points[1]),而非 sourceAnchor 字段—— * 这样在 path 已经经过手动编辑、首段方向与锚点 outward 不一致时仍能正确插入。 * * 路径点数 +2;新增 2 个内部段(stub 段 + 「被拖动段」)。被拖动段的初始另一轴 * 坐标与原首段重合(视觉直线),后续由拖动调整。 */ export declare function applySourceStub(points: [number, number][], stubLength: number | undefined, /** @deprecated 仅向后兼容,实际方向由 path 首段决定 */ sourceAnchor?: FloorMapEdgeAnchor): [number, number][]; /** * 对称地在路径末端插入 stub 转角。方向取自 *path 末段实际指向*。 * * 路径点数 +2;新增 2 个内部段(「被拖动段」 + stub 段)。 */ export declare function applyTargetStub(points: [number, number][], stubLength: number | undefined, /** @deprecated 仅向后兼容,实际方向由 path 末段决定 */ targetAnchor?: FloorMapEdgeAnchor): [number, number][]; export declare function applyMidSegmentOverrides(points: [number, number][], overrides: (number | null | undefined)[] | undefined): [number, number][]; /** * 返回正交折线中所有「内部段」的元信息(不含首段与末段), * 供 UI 渲染中段手柄使用。 */ export interface EdgeInnerSegmentInfo { /** 该内部段在 `midSegmentPositions` 中的索引(0 = path 的第 2 段) */ index: number; /** path.points 中该段起点的下标(终点为 i+1) */ pointIndex: number; axis: 'h' | 'v'; /** 段的主轴坐标:水平段 = y,竖直段 = x */ position: number; /** 段中点坐标(用于摆放手柄) */ midX: number; midY: number; /** 段长度(绝对值),用于判定是否值得渲染手柄(防止极短段误触) */ length: number; } export declare function getEdgeInnerSegments(points: [number, number][]): EdgeInnerSegmentInfo[]; export declare function buildEdgeRoute(edge: FloorMapEdge, rectsById: Map, options?: BuildEdgeRouteOptions): FloorMapEdgeRoute | null; /** * 去掉路径中"三点共线"的冗余中间点(比如 routeOrthogonal 里 srcPt → sOut → * 中段折点都在同一 y 上时,sOut 是冗余的)。 * * 保留零长度段:用户拖动 first/last 段创建 stub 时,stubEnd 与 midCorner 短暂 * 重合(零长度),不能被合并掉,否则会立刻丢失新插入的转角结构。 */ export declare function simplifyCollinearPath(points: [number, number][]): [number, number][]; /** * 按 edge 顺序分配交替偏移,让落到同一通道的多条线错开。 * 序列:0, +step, -step, +2*step, -2*step, ... */ export declare function getEdgeChannelOffset(index: number, step?: number): number; /** 命中检测:点到折线最近距离 */ export declare function distanceToPolyline(px: number, py: number, points: [number, number][]): number; export declare function hitTestEdgeAtPoint(px: number, py: number, routes: FloorMapEdgeRoute[], threshold?: number): string | null; export { ANCHORS as FLOOR_MAP_EDGE_ANCHORS };