R2:底板 / 刀片布局布线脚本、快照、截图与规划文档更新(含外壳、打印件、布线归档)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
这个提交包含在:
+67
-3
@@ -7,9 +7,12 @@ u"""把板上所有**直角走线**改成 45° 斜切(M7 收尾)。
|
||||
|
||||
## 为什么可以放心做
|
||||
|
||||
45° 斜切是**只去铜、不加铜**:新的斜边完全落在原来那个直角的三角形里面,
|
||||
所以任何「铜到铜」的净距只会变大不会变小 —— 不需要重跑布线器,也不会引入新的 DRC。
|
||||
(真正会变的只有线长,短一点点。)
|
||||
~~45° 斜切是只去铜、不加铜~~ —— ⛔ **2026-09-11 更正:这句只对拐角的外侧成立。**
|
||||
斜边的中心线落在原直角的三角形里,但它有线宽:斜边**凹侧**那条边会越过原来 L 形的内角、
|
||||
往凹侧伸进去(切深 d = K×线宽,1.19 宽的 +12V 线伸进去约 1 mm)—— 那是**加铜**。
|
||||
刀片 90×68 那一轮就这么压上了凹侧的一个 `$3N30` 过孔(DRC 导线 / 钻孔到过孔 0 mil);
|
||||
M7 全是细线,碰巧没出事。⇒ `corners()` 现在对每条新斜边按精确几何校核
|
||||
(异网同层线 / 所有过孔 / 异网同层焊盘 ≥ 规则净距 0.15),不够就不切(统计里的「凹侧净距不够」)。
|
||||
|
||||
## 判据(哪些拐点**不**动)
|
||||
|
||||
@@ -106,6 +109,61 @@ def ang(p, q):
|
||||
return math.atan2(q[1] - p[1], q[0] - p[0])
|
||||
|
||||
|
||||
CLR_CHECK = 0.15 # 设计规则的安全间距(新斜边的校核判据)
|
||||
VIA_R = 0.3
|
||||
|
||||
|
||||
def _step(p, o, d):
|
||||
L = math.hypot(o[0] - p[0], o[1] - p[1])
|
||||
return (p[0] + (o[0] - p[0]) / L * d, p[1] + (o[1] - p[1]) / L * d)
|
||||
|
||||
|
||||
def _pt_seg(px, py, a, b):
|
||||
dx, dy = b[0] - a[0], b[1] - a[1]
|
||||
L2 = dx * dx + dy * dy
|
||||
t = 0.0 if L2 < 1e-12 else max(0.0, min(1.0, ((px - a[0]) * dx + (py - a[1]) * dy) / L2))
|
||||
return math.hypot(px - (a[0] + t * dx), py - (a[1] + t * dy))
|
||||
|
||||
|
||||
def _seg_seg(a, b, c, e):
|
||||
def cr(o, p, q):
|
||||
return (p[0] - o[0]) * (q[1] - o[1]) - (p[1] - o[1]) * (q[0] - o[0])
|
||||
d1, d2, d3, d4 = cr(c, e, a), cr(c, e, b), cr(a, b, c), cr(a, b, e)
|
||||
if ((d1 > 0) != (d2 > 0)) and ((d3 > 0) != (d4 > 0)):
|
||||
return 0.0
|
||||
return min(_pt_seg(a[0], a[1], c, e), _pt_seg(b[0], b[1], c, e),
|
||||
_pt_seg(c[0], c[1], a, b), _pt_seg(e[0], e[1], a, b))
|
||||
|
||||
|
||||
def _seg_rect(a, b, r):
|
||||
for p in (a, b):
|
||||
if r[0] <= p[0] <= r[2] and r[1] <= p[1] <= r[3]:
|
||||
return 0.0
|
||||
corners4 = [(r[0], r[1]), (r[2], r[1]), (r[2], r[3]), (r[0], r[3])]
|
||||
return min(_seg_seg(a, b, corners4[k], corners4[(k + 1) % 4]) for k in range(4))
|
||||
|
||||
|
||||
def _clear(q1, q2, w, lay, net, i, j, segs, vias, pads):
|
||||
u"""新斜边 q1→q2(宽 w)离异网同层的线、所有过孔、异网同层焊盘是否都 ≥ CLR_CHECK。
|
||||
|
||||
过孔在 fetch() 里不带网名 ⇒ 一律当障碍(保守:本网的孔若恰好在凹侧,这个角就不切)。"""
|
||||
for k, s in enumerate(segs):
|
||||
if k in (i, j) or s['lay'] != lay or s['net'] == net:
|
||||
continue
|
||||
if _seg_seg(q1, q2, s['a'], s['b']) < w / 2.0 + s['w'] / 2.0 + CLR_CHECK:
|
||||
return False
|
||||
for (x, y) in vias:
|
||||
if _pt_seg(x, y, q1, q2) < w / 2.0 + VIA_R + CLR_CHECK:
|
||||
return False
|
||||
for p in pads:
|
||||
if p[6] != lay or p[7] == net:
|
||||
continue
|
||||
r = (p[2] - p[4] / 2.0, p[3] - p[5] / 2.0, p[2] + p[4] / 2.0, p[3] + p[5] / 2.0)
|
||||
if _seg_rect(q1, q2, r) < w / 2.0 + CLR_CHECK:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def corners(segs, vias, pads):
|
||||
u"""找出所有可切的直角拐点 -> [(拐点, 段i, 段j, d)]。"""
|
||||
key((0.0, 0.0), reset=True) # 每次重建归并表(同一进程里可能跑两轮)
|
||||
@@ -159,6 +217,10 @@ def corners(segs, vias, pads):
|
||||
if d < 0.1:
|
||||
stats['short'] += 1
|
||||
continue
|
||||
# ⛔ 新斜边的凹侧会加铜(模块头的更正)⇒ 先按精确几何校核,不够就不切
|
||||
if not _clear(_step(pi, oi, d), _step(pi, oj, d), w, lay, si['net'], i, j, segs, vias, pads):
|
||||
stats['clear'] = stats.get('clear', 0) + 1
|
||||
continue
|
||||
out.append((pi, i, j, d, oi, oj, w))
|
||||
return out, stats
|
||||
|
||||
@@ -214,6 +276,8 @@ def one_pass(rnd=1):
|
||||
print(u'可切的直角拐点:%d 个' % len(cuts))
|
||||
print(u'跳过:坐在孔/盘上 %d,三岔口 %d,两段线宽不同 %d,腿太短 %d(非直角拐点 %d 个不算错)'
|
||||
% (stats['via_pad'], stats['branch'], stats['width'], stats['short'], stats['not90']))
|
||||
if stats.get('clear'):
|
||||
print(u'跳过:新斜边凹侧净距不够 %d(< %.2f mm)' % (stats['clear'], CLR_CHECK))
|
||||
if '--push' not in sys.argv:
|
||||
print(u'(没加 --push,没有下发)')
|
||||
return False
|
||||
|
||||
在新工单中引用
屏蔽一个用户