Fix coordinate precision and per-schematic sidecar isolation
Reduce _rc() and transform_pin_to_schematic() rounding from 3 to 2 decimal places to match KiCad's 0.01mm coordinate quantum — prevents union-find misses when wire endpoints and sexp-parsed pin positions differ at the sub-quantum level. Use schematic stem as subdirectory inside .mckicad/ so multi-sheet analysis outputs (connectivity.json, etc.) don't collide.
This commit is contained in:
parent
b347679c67
commit
52ff054f43
@ -92,9 +92,11 @@ def _require_kicad_cli() -> tuple[str, None] | tuple[None, dict[str, Any]]:
|
|||||||
|
|
||||||
|
|
||||||
def _sidecar_dir(schematic_path: str) -> str:
|
def _sidecar_dir(schematic_path: str) -> str:
|
||||||
"""Return the .mckicad/ directory next to a schematic, creating it if needed."""
|
"""Return the .mckicad/{stem}/ directory next to a schematic, creating it if needed."""
|
||||||
parent = os.path.dirname(os.path.abspath(schematic_path))
|
abs_sch = os.path.abspath(schematic_path)
|
||||||
sidecar = os.path.join(parent, ".mckicad")
|
parent = os.path.dirname(abs_sch)
|
||||||
|
stem = os.path.splitext(os.path.basename(abs_sch))[0]
|
||||||
|
sidecar = os.path.join(parent, ".mckicad", stem)
|
||||||
os.makedirs(sidecar, exist_ok=True)
|
os.makedirs(sidecar, exist_ok=True)
|
||||||
return sidecar
|
return sidecar
|
||||||
|
|
||||||
@ -150,7 +152,7 @@ def _build_connectivity(
|
|||||||
|
|
||||||
def _rc(x: float, y: float) -> tuple[float, float]:
|
def _rc(x: float, y: float) -> tuple[float, float]:
|
||||||
"""Round coordinates for stable floating-point comparison."""
|
"""Round coordinates for stable floating-point comparison."""
|
||||||
return (round(x, 3), round(y, 3))
|
return (round(x, 2), round(y, 2))
|
||||||
|
|
||||||
def _coord_from_point(pt: Any) -> tuple[float, float]:
|
def _coord_from_point(pt: Any) -> tuple[float, float]:
|
||||||
"""Extract (x, y) from a Point-like object or sequence."""
|
"""Extract (x, y) from a Point-like object or sequence."""
|
||||||
|
|||||||
@ -18,7 +18,12 @@ def write_detail_file(schematic_path: str | None, filename: str, data: Any) -> s
|
|||||||
"""Write large result data to a .mckicad/ sidecar directory.
|
"""Write large result data to a .mckicad/ sidecar directory.
|
||||||
|
|
||||||
When ``schematic_path`` is provided, the sidecar directory is created
|
When ``schematic_path`` is provided, the sidecar directory is created
|
||||||
next to the schematic file. When ``None``, falls back to CWD.
|
next to the schematic file, inside a subdirectory named after the
|
||||||
|
schematic's stem (e.g. ``.mckicad/power/connectivity.json``). This
|
||||||
|
prevents multi-sheet designs from overwriting each other's output.
|
||||||
|
|
||||||
|
When ``None``, falls back to a flat ``.mckicad/`` in CWD (used for
|
||||||
|
search results that have no schematic context).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
schematic_path: Path to a .kicad_sch file (sidecar dir created next to it),
|
schematic_path: Path to a .kicad_sch file (sidecar dir created next to it),
|
||||||
@ -29,8 +34,13 @@ def write_detail_file(schematic_path: str | None, filename: str, data: Any) -> s
|
|||||||
Returns:
|
Returns:
|
||||||
Absolute path to the written file.
|
Absolute path to the written file.
|
||||||
"""
|
"""
|
||||||
parent_dir = os.path.dirname(os.path.abspath(schematic_path)) if schematic_path else os.getcwd()
|
if schematic_path:
|
||||||
sidecar_dir = os.path.join(parent_dir, ".mckicad")
|
abs_sch = os.path.abspath(schematic_path)
|
||||||
|
parent_dir = os.path.dirname(abs_sch)
|
||||||
|
stem = os.path.splitext(os.path.basename(abs_sch))[0]
|
||||||
|
sidecar_dir = os.path.join(parent_dir, ".mckicad", stem)
|
||||||
|
else:
|
||||||
|
sidecar_dir = os.path.join(os.getcwd(), ".mckicad")
|
||||||
os.makedirs(sidecar_dir, exist_ok=True)
|
os.makedirs(sidecar_dir, exist_ok=True)
|
||||||
|
|
||||||
out_path = os.path.join(sidecar_dir, filename)
|
out_path = os.path.join(sidecar_dir, filename)
|
||||||
|
|||||||
@ -162,7 +162,7 @@ def transform_pin_to_schematic(
|
|||||||
ry = px * sin_r + py * cos_r
|
ry = px * sin_r + py * cos_r
|
||||||
|
|
||||||
# Apply position offset
|
# Apply position offset
|
||||||
return (round(comp_x + rx, 3), round(comp_y + ry, 3))
|
return (round(comp_x + rx, 2), round(comp_y + ry, 2))
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tests.conftest import requires_sch_api
|
from tests.conftest import requires_sch_api
|
||||||
|
|
||||||
|
|
||||||
@ -120,7 +121,7 @@ def test_get_schematic_hierarchy(populated_schematic):
|
|||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_file_output_infrastructure(tmp_output_dir):
|
def test_file_output_infrastructure(tmp_output_dir):
|
||||||
"""write_detail_file should create .mckicad sidecar directory and file."""
|
"""write_detail_file should create .mckicad/{stem}/ sidecar directory and file."""
|
||||||
from mckicad.utils.file_utils import write_detail_file
|
from mckicad.utils.file_utils import write_detail_file
|
||||||
|
|
||||||
fake_sch = os.path.join(tmp_output_dir, "test.kicad_sch")
|
fake_sch = os.path.join(tmp_output_dir, "test.kicad_sch")
|
||||||
@ -132,12 +133,12 @@ def test_file_output_infrastructure(tmp_output_dir):
|
|||||||
|
|
||||||
assert os.path.isfile(path)
|
assert os.path.isfile(path)
|
||||||
assert ".mckicad" in path
|
assert ".mckicad" in path
|
||||||
assert path.endswith("test_output.json")
|
assert os.path.join(".mckicad", "test", "test_output.json") in path
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
def test_file_output_cwd_fallback(tmp_output_dir, monkeypatch):
|
def test_file_output_cwd_fallback(tmp_output_dir, monkeypatch):
|
||||||
"""write_detail_file with None path should use CWD."""
|
"""write_detail_file with None path should use flat CWD/.mckicad/."""
|
||||||
from mckicad.utils.file_utils import write_detail_file
|
from mckicad.utils.file_utils import write_detail_file
|
||||||
|
|
||||||
monkeypatch.chdir(tmp_output_dir)
|
monkeypatch.chdir(tmp_output_dir)
|
||||||
@ -145,3 +146,25 @@ def test_file_output_cwd_fallback(tmp_output_dir, monkeypatch):
|
|||||||
|
|
||||||
assert os.path.isfile(path)
|
assert os.path.isfile(path)
|
||||||
assert ".mckicad" in path
|
assert ".mckicad" in path
|
||||||
|
# None path stays flat — no stem subdirectory
|
||||||
|
assert path.endswith(os.path.join(".mckicad", "test_cwd.json"))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.unit
|
||||||
|
def test_sidecar_per_schematic_isolation(tmp_output_dir):
|
||||||
|
"""Detail files for different schematics land in separate subdirectories."""
|
||||||
|
from mckicad.utils.file_utils import write_detail_file
|
||||||
|
|
||||||
|
sch_a = os.path.join(tmp_output_dir, "power.kicad_sch")
|
||||||
|
sch_b = os.path.join(tmp_output_dir, "esp32_p4_core.kicad_sch")
|
||||||
|
open(sch_a, "w").close()
|
||||||
|
open(sch_b, "w").close()
|
||||||
|
|
||||||
|
path_a = write_detail_file(sch_a, "connectivity.json", {"nets": {}})
|
||||||
|
path_b = write_detail_file(sch_b, "connectivity.json", {"nets": {}})
|
||||||
|
|
||||||
|
assert os.path.isfile(path_a)
|
||||||
|
assert os.path.isfile(path_b)
|
||||||
|
assert path_a != path_b
|
||||||
|
assert os.path.join(".mckicad", "power", "connectivity.json") in path_a
|
||||||
|
assert os.path.join(".mckicad", "esp32_p4_core", "connectivity.json") in path_b
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user