fix: address review issues in get_wire_connections
- Fix critical bug: use pin_data[0]/[1] instead of pin_data["x"]/["y"] (get_all_symbol_pins returns List[float], not dict) - Use index-based wire tracking to avoid fragile float list equality - Check all polyline points (not just endpoints) during flood-fill - Add spatial index (0.05mm grid) to replace O(n²) frontier scan - Skip already-processed refs to avoid redundant calls for multi-unit symbols - Include wires_out in early return when schematic has no symbols - Add get_wire_connections schema entry to tool_schemas.py Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2361,39 +2361,72 @@ class KiCADInterface:
|
|||||||
|
|
||||||
# Step 1: Find all seed wires that touch the given point (start or end)
|
# Step 1: Find all seed wires that touch the given point (start or end)
|
||||||
query_point = [x, y]
|
query_point = [x, y]
|
||||||
seed_wires = [
|
seed_indices = set(
|
||||||
pts for pts in all_wires
|
i for i, pts in enumerate(all_wires)
|
||||||
if points_coincide(pts[0], query_point) or points_coincide(pts[-1], query_point)
|
if any(points_coincide(pt, query_point) for pt in pts)
|
||||||
]
|
)
|
||||||
|
|
||||||
if not seed_wires:
|
if not seed_indices:
|
||||||
return {
|
return {
|
||||||
"success": False,
|
"success": False,
|
||||||
"message": f"No wire found at ({x},{y}) within {tolerance}mm tolerance",
|
"message": f"No wire found at ({x},{y}) within {tolerance}mm tolerance",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Step 2: Flood-fill through connected wires
|
# Step 2: Flood-fill through connected wires
|
||||||
connected_wires = list(seed_wires)
|
connected_indices = set(seed_indices)
|
||||||
frontier = set((pt[0], pt[1]) for pts in seed_wires for pt in pts)
|
frontier = set((pt[0], pt[1]) for i in seed_indices for pt in all_wires[i])
|
||||||
remaining = [w for w in all_wires if w not in seed_wires]
|
|
||||||
|
# Spatial index: grid-snapped dict for O(1) proximity lookup
|
||||||
|
import math
|
||||||
|
GRID = 0.05 # mm, matches KiCAD schematic grid
|
||||||
|
grid_radius = math.ceil(tolerance / GRID) # cells to check per axis
|
||||||
|
|
||||||
|
def _grid_key(x_coord, y_coord):
|
||||||
|
return (round(x_coord / GRID), round(y_coord / GRID))
|
||||||
|
|
||||||
|
# frontier_grid maps grid cell -> list of (x, y) frontier points in that cell
|
||||||
|
frontier_grid = {}
|
||||||
|
for fp in frontier:
|
||||||
|
key = _grid_key(fp[0], fp[1])
|
||||||
|
frontier_grid.setdefault(key, []).append(fp)
|
||||||
|
|
||||||
|
def _frontier_has_neighbour(px, py):
|
||||||
|
"""Check if any frontier point is within tolerance of (px, py)."""
|
||||||
|
cx, cy = _grid_key(px, py)
|
||||||
|
for dx in range(-grid_radius, grid_radius + 1):
|
||||||
|
for dy in range(-grid_radius, grid_radius + 1):
|
||||||
|
cell = (cx + dx, cy + dy)
|
||||||
|
cell_points = frontier_grid.get(cell)
|
||||||
|
if cell_points:
|
||||||
|
for fp in cell_points:
|
||||||
|
if abs(px - fp[0]) < tolerance and abs(py - fp[1]) < tolerance:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _add_to_frontier_grid(pt):
|
||||||
|
key = _grid_key(pt[0], pt[1])
|
||||||
|
frontier_grid.setdefault(key, []).append(pt)
|
||||||
|
|
||||||
|
remaining_indices = [i for i in range(len(all_wires)) if i not in seed_indices]
|
||||||
changed = True
|
changed = True
|
||||||
while changed:
|
while changed:
|
||||||
changed = False
|
changed = False
|
||||||
still_remaining = []
|
still_remaining = []
|
||||||
for pts in remaining:
|
for i in remaining_indices:
|
||||||
wire_endpoints = {(pts[0][0], pts[0][1]), (pts[-1][0], pts[-1][1])}
|
pts = all_wires[i]
|
||||||
if any(
|
wire_points = [(pt[0], pt[1]) for pt in pts]
|
||||||
points_coincide(list(ep), list(fp))
|
if any(_frontier_has_neighbour(wp[0], wp[1]) for wp in wire_points):
|
||||||
for ep in wire_endpoints
|
connected_indices.add(i)
|
||||||
for fp in frontier
|
|
||||||
):
|
|
||||||
connected_wires.append(pts)
|
|
||||||
for pt in pts:
|
for pt in pts:
|
||||||
frontier.add((pt[0], pt[1]))
|
p = (pt[0], pt[1])
|
||||||
|
frontier.add(p)
|
||||||
|
_add_to_frontier_grid(p)
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
still_remaining.append(pts)
|
still_remaining.append(i)
|
||||||
remaining = still_remaining
|
remaining_indices = still_remaining
|
||||||
|
|
||||||
|
connected_wires = [all_wires[i] for i in connected_indices]
|
||||||
|
|
||||||
# Step 3: Collect all points from connected wires
|
# Step 3: Collect all points from connected wires
|
||||||
connected_points = set()
|
connected_points = set()
|
||||||
@@ -2402,12 +2435,17 @@ class KiCADInterface:
|
|||||||
connected_points.add((pt[0], pt[1]))
|
connected_points.add((pt[0], pt[1]))
|
||||||
|
|
||||||
# Step 4: Find component pins at connected points
|
# Step 4: Find component pins at connected points
|
||||||
|
wires_out = [
|
||||||
|
{"start": {"x": pts[0][0], "y": pts[0][1]}, "end": {"x": pts[-1][0], "y": pts[-1][1]}}
|
||||||
|
for pts in connected_wires
|
||||||
|
]
|
||||||
if not hasattr(schematic, "symbol"):
|
if not hasattr(schematic, "symbol"):
|
||||||
return {"success": True, "pins": []}
|
return {"success": True, "pins": [], "wires": wires_out}
|
||||||
|
|
||||||
locator = PinLocator()
|
locator = PinLocator()
|
||||||
pins = []
|
pins = []
|
||||||
seen = set()
|
seen = set()
|
||||||
|
processed_refs = set()
|
||||||
|
|
||||||
for symbol in schematic.symbol:
|
for symbol in schematic.symbol:
|
||||||
if not hasattr(symbol.property, "Reference"):
|
if not hasattr(symbol.property, "Reference"):
|
||||||
@@ -2415,13 +2453,16 @@ class KiCADInterface:
|
|||||||
ref = symbol.property.Reference.value
|
ref = symbol.property.Reference.value
|
||||||
if ref.startswith("_TEMPLATE"):
|
if ref.startswith("_TEMPLATE"):
|
||||||
continue
|
continue
|
||||||
|
if ref in processed_refs:
|
||||||
|
continue
|
||||||
|
processed_refs.add(ref)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
all_pins = locator.get_all_symbol_pins(Path(schematic_path), ref)
|
all_pins = locator.get_all_symbol_pins(Path(schematic_path), ref)
|
||||||
if not all_pins:
|
if not all_pins:
|
||||||
continue
|
continue
|
||||||
for pin_num, pin_data in all_pins.items():
|
for pin_num, pin_data in all_pins.items():
|
||||||
pin_loc = [pin_data["x"], pin_data["y"]]
|
pin_loc = [pin_data[0], pin_data[1]]
|
||||||
for cp in connected_points:
|
for cp in connected_points:
|
||||||
if points_coincide(pin_loc, list(cp)):
|
if points_coincide(pin_loc, list(cp)):
|
||||||
key = (ref, pin_num)
|
key = (ref, pin_num)
|
||||||
@@ -2432,10 +2473,6 @@ class KiCADInterface:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Error checking pins for {ref}: {e}")
|
logger.warning(f"Error checking pins for {ref}: {e}")
|
||||||
|
|
||||||
wires_out = [
|
|
||||||
{"start": {"x": pts[0][0], "y": pts[0][1]}, "end": {"x": pts[-1][0], "y": pts[-1][1]}}
|
|
||||||
for pts in connected_wires
|
|
||||||
]
|
|
||||||
return {"success": True, "pins": pins, "wires": wires_out}
|
return {"success": True, "pins": pins, "wires": wires_out}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -1471,6 +1471,29 @@ SCHEMATIC_TOOLS = [
|
|||||||
"required": ["schematicPath", "netName"],
|
"required": ["schematicPath", "netName"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "get_wire_connections",
|
||||||
|
"title": "Get Wire Connections",
|
||||||
|
"description": "Returns all wires and component pins connected to the wire at a given point, by flood-filling through touching wires.",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"schematicPath": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Path to schematic file"
|
||||||
|
},
|
||||||
|
"x": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "X coordinate of the point on the wire"
|
||||||
|
},
|
||||||
|
"y": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "Y coordinate of the point on the wire"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["schematicPath", "x", "y"]
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "get_schematic_pin_locations",
|
"name": "get_schematic_pin_locations",
|
||||||
"title": "Get Schematic Pin Locations",
|
"title": "Get Schematic Pin Locations",
|
||||||
|
|||||||
Reference in New Issue
Block a user