/** * Wire Path Generator * * Generates SVG path strings for wires with orthogonal routing (only horizontal/vertical segments). * Phase 1: Simple L-shape * Phase 2: Multi-segment with control points * Phase 3: A* pathfinding integration */ import type { Wire, WireControlPoint } from '../types/wire'; /** * Generates an SVG path string for a wire. * Routes wires using orthogonal paths (90-degree angles only). * * @param wire - The wire object containing endpoints and control points * @returns SVG path string (e.g., "M 10 20 L 30 20 L 30 50") */ export function generateWirePath(wire: Wire): string { const { start, end, controlPoints } = wire; if (controlPoints.length === 0) { // Phase 1: Simple L-shape routing return generateSimplePath(start.x, start.y, end.x, end.y); } else { // Phase 2: Multi-segment with control points return generateMultiSegmentPath(start, controlPoints, end); } } /** * Phase 1: Generates a simple L-shaped path between two points. * Prioritizes horizontal-first routing (goes horizontal, then vertical, then horizontal). * * Pattern: * Start → [horizontal] → midpoint → [vertical] → midpoint → [horizontal] → End * * @param x1 - Start X coordinate * @param y1 - Start Y coordinate * @param x2 - End X coordinate * @param y2 - End Y coordinate * @returns SVG path string */ function generateSimplePath(x1: number, y1: number, x2: number, y2: number): string { // Calculate midpoint X (for L-shape bend) const midX = x1 + (x2 - x1) / 2; // Create horizontal-first L-shape path // Format: M x1,y1 L midX,y1 L midX,y2 L x2,y2 return `M ${x1} ${y1} L ${midX} ${y1} L ${midX} ${y2} L ${x2} ${y2}`; } /** * Phase 2: Generates path with multiple control points. * All segments are constrained to horizontal or vertical only (orthogonal routing). * * @param start - Start endpoint { x, y } * @param controlPoints - Array of control points * @param end - End endpoint { x, y } * @returns SVG path string */ function generateMultiSegmentPath( start: { x: number; y: number }, controlPoints: WireControlPoint[], end: { x: number; y: number } ): string { let path = `M ${start.x} ${start.y}`; // Control points already represent exact corners, so connect them sequentially for (let i = 0; i < controlPoints.length; i++) { const cp = controlPoints[i]; path += ` L ${cp.x} ${cp.y}`; } // Connect last control point to end path += ` L ${end.x} ${end.y}`; return path; } /** * Calculates the total length of a wire path (useful for rendering and optimization). * * @param wire - The wire object * @returns Total path length in pixels */ export function calculateWireLength(wire: Wire): number { const { start, end, controlPoints } = wire; let totalLength = 0; let prevPoint = start; for (const cp of controlPoints) { totalLength += Math.abs(cp.x - prevPoint.x) + Math.abs(cp.y - prevPoint.y); prevPoint = cp; } totalLength += Math.abs(end.x - prevPoint.x) + Math.abs(end.y - prevPoint.y); return totalLength; }