"""占位登记与重叠检测。 原理图上画的每一样东西都登记成一个矩形,最后逐对相交检查。 要拦的是三类:文字压文字、文字压器件/走线、器件压器件。 走线之间相交是正常的(交叉不等于连接),不算错。 """ 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()