mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 22:04:56 +00:00
145 lines
5.1 KiB
Python
145 lines
5.1 KiB
Python
import sys
|
|
from pathlib import Path
|
|
import zipfile
|
|
import pytest
|
|
|
|
EXPECTED_DIR = Path(__file__).resolve().parent / "expected"
|
|
|
|
# Add repository root to path
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from boardforge import PCB, Layer
|
|
|
|
|
|
def test_demo_script_equivalent(tmp_path):
|
|
base = ROOT
|
|
font_path = base / "fonts" / "RobotoMono.ttf"
|
|
svg_path = base / "graphics" / "torch.svg"
|
|
|
|
board = PCB(width=80, height=60)
|
|
board.set_layer_stack([
|
|
Layer.TOP_COPPER.value,
|
|
Layer.BOTTOM_COPPER.value,
|
|
Layer.TOP_SILK.value,
|
|
Layer.BOTTOM_SILK.value,
|
|
])
|
|
|
|
bat = board.add_component("CR2032", ref="BT1", at=(40, 10))
|
|
bat.add_pin("VCC", dx=0, dy=0)
|
|
bat.add_pin("GND", dx=10, dy=0)
|
|
bat.add_pad("VCC", dx=0, dy=0, w=2, h=2)
|
|
bat.add_pad("GND", dx=10, dy=0, w=2, h=2)
|
|
|
|
sw = board.add_component("SWITCH", ref="SW1", at=(40, 20))
|
|
sw.add_pin("A", dx=-2, dy=0)
|
|
sw.add_pin("B", dx=2, dy=0)
|
|
sw.add_pad("A", dx=-2, dy=0, w=1.8, h=1.8)
|
|
sw.add_pad("B", dx=2, dy=0, w=1.8, h=1.8)
|
|
|
|
resistor_positions = [(20, 35), (40, 35), (60, 35)]
|
|
resistors = []
|
|
for i, pos in enumerate(resistor_positions):
|
|
r = board.add_component("RESISTOR", ref=f"R{i+1}", at=pos)
|
|
r.add_pin("A", dx=-3, dy=0)
|
|
r.add_pin("B", dx=3, dy=0)
|
|
r.add_pad("A", dx=-3, dy=0, w=1.6, h=1.6)
|
|
r.add_pad("B", dx=3, dy=0, w=1.6, h=1.6)
|
|
resistors.append(r)
|
|
|
|
led_positions = [
|
|
(20, 50, 45),
|
|
(40, 50, 0),
|
|
(60, 50, -45),
|
|
]
|
|
leds = []
|
|
for i, (x, y, ang) in enumerate(led_positions):
|
|
d = board.add_component("LED", ref=f"D{i+1}", at=(x, y), rotation=ang)
|
|
d.add_pin("A", dx=0, dy=-2)
|
|
d.add_pin("K", dx=0, dy=2)
|
|
d.add_pad("A", dx=0, dy=-2, w=1.6, h=1.6)
|
|
d.add_pad("K", dx=0, dy=2, w=1.6, h=1.6)
|
|
leds.append(d)
|
|
|
|
board.route_trace("BT1:VCC", "SW1:A")
|
|
for r in resistors:
|
|
board.route_trace("SW1:B", f"{r.ref}:A")
|
|
for r, d in zip(resistors, leds):
|
|
board.route_trace(f"{r.ref}:B", f"{d.ref}:A")
|
|
for d in leds:
|
|
board.route_trace(f"{d.ref}:K", "BT1:GND")
|
|
|
|
for c in [bat, sw] + resistors + leds:
|
|
board.add_text_ttf(
|
|
c.ref,
|
|
font_path=str(font_path),
|
|
at=(c.at[0]-4, c.at[1]-5),
|
|
size=1.2,
|
|
layer=Layer.TOP_SILK.value,
|
|
)
|
|
|
|
if svg_path.exists():
|
|
board.add_svg_graphic(str(svg_path), layer=Layer.TOP_SILK.value, scale=1.2, at=(5, 5))
|
|
|
|
board.save_svg_previews(tmp_path)
|
|
zip_path = tmp_path / "demo_output.zip"
|
|
board.export_gerbers(zip_path)
|
|
|
|
assert zip_path.exists()
|
|
with zipfile.ZipFile(zip_path) as z:
|
|
names = set(z.namelist())
|
|
assert {"GTL.gbr", "GBL.gbr", "GTO.gbr", "GBO.gbr"}.issubset(names)
|
|
assert {"preview_top.svg", "preview_bottom.svg", "preview_top.png", "preview_bottom.png"}.issubset(names)
|
|
gtl_data = z.read("GTL.gbr").decode()
|
|
gbl_data = z.read("GBL.gbr").decode()
|
|
gto_data = z.read("GTO.gbr").decode()
|
|
gbo_data = z.read("GBO.gbr").decode()
|
|
top_png = z.read("preview_top.png") if "preview_top.png" in names else b""
|
|
|
|
expected_gtl = (EXPECTED_DIR / "demo_GTL.gbr").read_text()
|
|
expected_gbl = (EXPECTED_DIR / "demo_GBL.gbr").read_text()
|
|
expected_gto = (EXPECTED_DIR / "demo_GTO.gbr").read_text()
|
|
expected_gbo = (EXPECTED_DIR / "demo_GBO.gbr").read_text()
|
|
|
|
if top_png:
|
|
from io import BytesIO
|
|
from PIL import Image
|
|
with Image.open(BytesIO(top_png)) as img:
|
|
assert img.size == (board.width * 10, board.height * 10)
|
|
assert img.getpixel((30, 30)) == (93, 34, 146, 255)
|
|
|
|
# verify pad colours and rings
|
|
for comp in [bat, sw] + resistors + leds:
|
|
for pad in comp.pads:
|
|
px = int(pad.x * 10)
|
|
py = int(pad.y * 10)
|
|
assert img.getpixel((px, py)) == (255, 193, 0, 255)
|
|
|
|
if abs(pad.w - pad.h) <= 0.1:
|
|
ring_r = int((int(pad.w * 10) + 6) // 2)
|
|
ring_px = px + ring_r - 2
|
|
if ring_px < img.width:
|
|
assert img.getpixel((ring_px, py)) == (255, 236, 128, 255)
|
|
|
|
# verify silkscreen text renders in white
|
|
for comp in [bat, sw] + resistors + leds:
|
|
tx = int((comp.at[0] - 4) * 10)
|
|
ty = int((comp.at[1] - 5) * 10)
|
|
found_white = False
|
|
for dx in range(-10, 11):
|
|
for dy in range(-10, 11):
|
|
x = tx + dx
|
|
y = ty + dy
|
|
if 0 <= x < img.width and 0 <= y < img.height:
|
|
if img.getpixel((x, y)) == (255, 255, 255, 255):
|
|
found_white = True
|
|
break
|
|
if found_white:
|
|
break
|
|
assert found_white
|
|
|
|
assert gtl_data == expected_gtl
|
|
assert gbl_data == expected_gbl
|
|
assert gto_data == expected_gto
|
|
assert gbo_data == expected_gbo
|