Files
skills-win/kicad-flow/scripts/schgen/layout.py
T
2026-10-15 01:59:40 +08:00

116 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""占位登记与重叠检测。
原理图上画的每一样东西都登记成一个矩形,最后逐对相交检查。
要拦的是三类:文字压文字、文字压器件/走线、器件压器件。
走线之间相交是正常的(交叉不等于连接),不算错。
"""
PAPER = {"A4": (297.0, 210.0), "A3": (420.0, 297.0), "A2": (594.0, 420.0), "A1": (841.0, 594.0)}
CH_W = 1.20 # 1.27 号字下 ASCII 单字符宽度(mm)。原来用 0.90 会漏报:DVBS2_dmod 的
# C405 "22u/35V" 实际压到隔壁 C406 的极板上而没报;1.10 又漏过 C412。宁可误报(G-0034)
CJK_W = 1.30 # 中文/全角字符宽度。KiCad 的笔画字体里全角字宽约等于字号,
# 按 ASCII 的 0.90 算会低估近 40% —— 09 RF 页就漏检过一处
# 中文说明压住端接电阻的重叠,所以按字符类型分别估(G-0034)。
def strw(s, size=1.27):
"""按字符类型估算字符串宽度(mm)"""
k = size / 1.27
return sum((CJK_W if ord(c) > 0x2E80 else CH_W) for c in s) * k
CH_H = 1.70 # 单行文字高度
LBL_PAD = 3.0 # 全局标签外框比文字多出来的量(箭头/边框)
def cat(what):
return what.split(":", 1)[0]
class Canvas:
def __init__(self, paper="A3", name=""):
w, h = PAPER[paper]
# KiCad 图框内沿约在四边 10mm 处;左边还有行号栏,底部整条留给标题栏
self.x0, self.y0 = 22.0, 14.0
self.x1, self.y1 = w - 14.0, h - 36.0
self.name, self.paper = name, paper
self.boxes = []
# ---- 登记 ----
def add(self, x0, y0, x1, y1, what):
if x1 < x0: x0, x1 = x1, x0
if y1 < y0: y0, y1 = y1, y0
self.boxes.append((x0, y0, x1, y1, what))
def add_sym(self, cx, cy, bbox, what, pad=0.0):
"""bbox 为符号坐标系(y 向上,已按放置角度旋转过)。"""
bx0, by0, bx1, by1 = bbox
self.add(cx + bx0 - pad, cy - by1 - pad, cx + bx1 + pad, cy - by0 + pad, what)
def add_text(self, x, y, s, justify="left", size=1.27, what=None):
k = size / 1.27
w = strw(s, size)
h = CH_H * k
if justify == "left":
x0, x1 = x, x + w
elif justify == "right":
x0, x1 = x - w, x
else:
x0, x1 = x - w / 2.0, x + w / 2.0
self.add(x0, y - h * 0.8, x1, y + h * 0.4, what or ("text:" + s[:18]))
def add_label(self, x, y, s, lrot, what=None):
"""全局标签:锚点在 (x,y),标签体朝 lrot 指定的方向伸出。
0=向右 180=向左 90=向上 270=向下(KiCad 文字角度约定)。"""
w = strw(s) + LBL_PAD
t = 1.15 # 标签体半高(1.27 号字 + 边框,实测约 2.1~2.4mm 高;排针 2.54 行距刚好放得下)
d = 0.4 # 标签体把锚点包在里面,这一小段不算和自己的引线冲突
if lrot == 0:
self.add(x + d, y - t, x + w, y + t, what)
elif lrot == 180:
self.add(x - w, y - t, x - d, y + t, what)
elif lrot == 90:
self.add(x - t, y - w, x + t, y - d, what)
else:
self.add(x - t, y + d, x + t, y + w, what)
def add_wire(self, x1, y1, x2, y2, what="wire"):
t = 0.25
self.add(min(x1, x2) - t, min(y1, y2) - t, max(x1, x2) + t, max(y1, y2) + t, what)
# ---- 检查 ----
def outside(self):
bad = []
for b in self.boxes:
if b[0] < self.x0 - 0.01 or b[1] < self.y0 - 0.01 or \
b[2] > self.x1 + 0.01 or b[3] > self.y1 + 0.01:
bad.append(b)
return bad
@staticmethod
def _care(a, b):
ca, cb = cat(a), cat(b)
txt = ("text", "lbl", "ref")
# 断路标记画在引脚端点上,本来就贴着符号和走线,只管它别压着文字
if "nc" in (ca, cb) or "jct" in (ca, cb):
return ca in txt or cb in txt
if ca in txt or cb in txt:
return True
return ca == "sym" and cb == "sym"
def overlaps(self):
res, bs = [], self.boxes
for i in range(len(bs)):
ax0, ay0, ax1, ay1, aw = bs[i]
for j in range(i + 1, len(bs)):
bx0, by0, bx1, by1, bw = bs[j]
if ax1 <= bx0 + 0.01 or bx1 <= ax0 + 0.01 or \
ay1 <= by0 + 0.01 or by1 <= ay0 + 0.01:
continue
if not self._care(aw, bw):
continue
res.append((aw, bw, round(ax0, 1), round(ay0, 1)))
return res
def report(self):
return self.outside(), self.overlaps()