fix: replace kicad-skip mutation with sexpdata in delete_schematic_wire
kicad-skip's write() serializes from original parsed data, silently discarding in-memory _elements mutations. Switch to the same sexpdata approach used by add_wire: parse, find matching wire, delete the entry, write back. Also adds WireManager.delete_wire() static method. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -340,6 +340,72 @@ class WireManager:
|
|||||||
logger.error(traceback.format_exc())
|
logger.error(traceback.format_exc())
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def delete_wire(schematic_path: Path, start_point: List[float], end_point: List[float],
|
||||||
|
tolerance: float = 0.5) -> bool:
|
||||||
|
"""
|
||||||
|
Delete a wire from the schematic matching given start/end coordinates.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
schematic_path: Path to .kicad_sch file
|
||||||
|
start_point: [x, y] coordinates for wire start
|
||||||
|
end_point: [x, y] coordinates for wire end
|
||||||
|
tolerance: Maximum coordinate difference to consider a match (mm)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if a wire was found and removed, False otherwise
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open(schematic_path, 'r', encoding='utf-8') as f:
|
||||||
|
sch_content = f.read()
|
||||||
|
|
||||||
|
sch_data = sexpdata.loads(sch_content)
|
||||||
|
|
||||||
|
sx, sy = start_point
|
||||||
|
ex, ey = end_point
|
||||||
|
|
||||||
|
for i, item in enumerate(sch_data):
|
||||||
|
if not (isinstance(item, list) and len(item) > 0 and item[0] == Symbol('wire')):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Extract pts from the wire s-expression
|
||||||
|
pts_list = None
|
||||||
|
for part in item[1:]:
|
||||||
|
if isinstance(part, list) and len(part) > 0 and part[0] == Symbol('pts'):
|
||||||
|
pts_list = part
|
||||||
|
break
|
||||||
|
|
||||||
|
if pts_list is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
xy_points = [p for p in pts_list[1:] if isinstance(p, list) and len(p) >= 3 and p[0] == Symbol('xy')]
|
||||||
|
if len(xy_points) < 2:
|
||||||
|
continue
|
||||||
|
|
||||||
|
x1, y1 = float(xy_points[0][1]), float(xy_points[0][2])
|
||||||
|
x2, y2 = float(xy_points[-1][1]), float(xy_points[-1][2])
|
||||||
|
|
||||||
|
match_fwd = (abs(x1 - sx) < tolerance and abs(y1 - sy) < tolerance and
|
||||||
|
abs(x2 - ex) < tolerance and abs(y2 - ey) < tolerance)
|
||||||
|
match_rev = (abs(x1 - ex) < tolerance and abs(y1 - ey) < tolerance and
|
||||||
|
abs(x2 - sx) < tolerance and abs(y2 - sy) < tolerance)
|
||||||
|
|
||||||
|
if match_fwd or match_rev:
|
||||||
|
del sch_data[i]
|
||||||
|
with open(schematic_path, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(sexpdata.dumps(sch_data))
|
||||||
|
logger.info(f"Deleted wire from {start_point} to {end_point}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
logger.warning(f"No matching wire found for {start_point} to {end_point}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error deleting wire: {e}")
|
||||||
|
import traceback
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_orthogonal_path(start: List[float], end: List[float],
|
def create_orthogonal_path(start: List[float], end: List[float],
|
||||||
prefer_horizontal_first: bool = True) -> List[List[float]]:
|
prefer_horizontal_first: bool = True) -> List[List[float]]:
|
||||||
|
|||||||
@@ -1898,43 +1898,13 @@ class KiCADInterface:
|
|||||||
if not schematic_path:
|
if not schematic_path:
|
||||||
return {"success": False, "message": "schematicPath is required"}
|
return {"success": False, "message": "schematicPath is required"}
|
||||||
|
|
||||||
schematic = SchematicManager.load_schematic(schematic_path)
|
from pathlib import Path
|
||||||
if not schematic:
|
from commands.wire_manager import WireManager
|
||||||
return {"success": False, "message": "Failed to load schematic"}
|
start_point = [start.get("x", 0), start.get("y", 0)]
|
||||||
|
end_point = [end.get("x", 0), end.get("y", 0)]
|
||||||
|
|
||||||
if not hasattr(schematic, "wire"):
|
deleted = WireManager.delete_wire(Path(schematic_path), start_point, end_point)
|
||||||
return {"success": False, "message": "Schematic has no wires"}
|
if deleted:
|
||||||
|
|
||||||
tolerance = 0.5
|
|
||||||
wire_to_remove = None
|
|
||||||
|
|
||||||
for wire in schematic.wire:
|
|
||||||
if not hasattr(wire, "pts") or not hasattr(wire.pts, "xy"):
|
|
||||||
continue
|
|
||||||
points = []
|
|
||||||
for point in wire.pts.xy:
|
|
||||||
if hasattr(point, "value"):
|
|
||||||
points.append([float(point.value[0]), float(point.value[1])])
|
|
||||||
|
|
||||||
if len(points) < 2:
|
|
||||||
continue
|
|
||||||
|
|
||||||
sx, sy = start.get("x", 0), start.get("y", 0)
|
|
||||||
ex, ey = end.get("x", 0), end.get("y", 0)
|
|
||||||
|
|
||||||
# Check both directions
|
|
||||||
match_fwd = (abs(points[0][0] - sx) < tolerance and abs(points[0][1] - sy) < tolerance and
|
|
||||||
abs(points[-1][0] - ex) < tolerance and abs(points[-1][1] - ey) < tolerance)
|
|
||||||
match_rev = (abs(points[0][0] - ex) < tolerance and abs(points[0][1] - ey) < tolerance and
|
|
||||||
abs(points[-1][0] - sx) < tolerance and abs(points[-1][1] - sy) < tolerance)
|
|
||||||
|
|
||||||
if match_fwd or match_rev:
|
|
||||||
wire_to_remove = wire
|
|
||||||
break
|
|
||||||
|
|
||||||
if wire_to_remove:
|
|
||||||
schematic.wire._elements.remove(wire_to_remove)
|
|
||||||
SchematicManager.save_schematic(schematic, schematic_path)
|
|
||||||
return {"success": True}
|
return {"success": True}
|
||||||
else:
|
else:
|
||||||
return {"success": False, "message": "No matching wire found"}
|
return {"success": False, "message": "No matching wire found"}
|
||||||
|
|||||||
Reference in New Issue
Block a user