文件

178 行
5.9 KiB
Python

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# -*- coding: utf-8 -*-
u"""给外壳件加**外轮廓圆角**(用户 2026-09-13 要求)。v2 重写。
⛔ v1 失败的真因(不是 API 坏了):
最小实验(纯长方体 12 条边)倒 4 条竖边 → 成功、直方图 `{3.0: 4}`。
但真实零件上:托盘 60 条边里选了 12 条、端板 34 条里选了 8 条,
`FeatureFillet3` 一律返回 **None**。
共同点是**选中的边里混进了做不出来的**:
· 托盘:USB-C 缺口的内角边 —— 缺口只有 4.2 深,R3 在窄槽内角会自相交;
· 端板:L 脚与板面的交线 —— 同理超出可行范围。
SolidWorks 对这种"整批里有一条做不出"的情况**整批放弃**,还不报错。
✅ v2 的三条对策:
1. **只倒外轮廓的角**:按实体包围盒筛,边的两个端点必须都落在包围盒的角上,
这样自动排除所有内部缺口/开口/台阶产生的边。
2. **逐条单独倒**:一条失败不拖垮其余,且能精确报出是哪条边做不出来。
3. **失败自动降半径**:R3 → R2 → R1.5 → R1 依次重试。
判据仍是**圆柱面半径直方图**(包围盒对圆角没有分辨力)。
用法:python add_fillets.py <零件文件名> [半径] [轴]
"""
import io
import os
import sys
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', write_through=True)
import pythoncom
import win32com.client as wc
DIR = u'D:\\DCDC\\新建文件夹\\5blades\\'
EMPTY = wc.VARIANT(pythoncom.VT_DISPATCH, None)
fname = sys.argv[1] if len(sys.argv) > 1 else 'Tray_v7.SLDPRT'
RAD0 = float(sys.argv[2]) if len(sys.argv) > 2 else 3.0
AXIS = (sys.argv[3] if len(sys.argv) > 3 else 'z').lower()
AI = {'x': 0, 'y': 1, 'z': 2}[AXIS]
PATH = DIR + fname
RETRY = [RAD0, 2.0, 1.5, 1.0]
def prop(o, n):
v = getattr(o, n)
return v() if type(v).__name__ in ('method', 'function', 'builtin_function_or_method') else v
def D(x):
return x if hasattr(x, '_oleobj_') else wc.Dispatch(x)
def P(*a):
print(*a)
def M(v):
return v / 1000.0
app = wc.GetActiveObject('SldWorks.Application')
if not os.path.exists(PATH):
P(u'⛔ 文件不存在 %s' % PATH); sys.exit(2)
part = None
for d in app.GetDocuments or []:
try:
if (prop(d, 'GetPathName') or '').lower() == PATH.lower():
part = d
break
except Exception:
pass
if part is None:
err = wc.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0)
warn = wc.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0)
part = app.OpenDoc6(PATH, 1, 0, '', err, warn)
P(u'打开 %s err=%s' % (fname, err.value))
if part is None:
P(u'⛔ 打不开'); sys.exit(3)
e0 = wc.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0)
app.ActivateDoc3(prop(part, 'GetTitle'), False, 0, e0)
part = app.ActiveDoc
fm = part.FeatureManager
P(u'零件 %s,目标 R%.1f,沿 %s 轴' % (prop(part, 'GetTitle'), RAD0, AXIS))
def body():
b = part.GetBodies2(0, True) or []
return D(b[0]) if b else None
def cyl_hist():
bd = body()
if bd is None:
return {}
rs = {}
for f in prop(bd, 'GetFaces'):
s = prop(D(f), 'GetSurface')
if prop(s, 'IsCylinder'):
r = round(list(prop(s, 'CylinderParams'))[6] * 1000, 2)
rs[r] = rs.get(r, 0) + 1
return dict(sorted(rs.items()))
before = cyl_hist()
bx = [round(v * 1000, 3) for v in prop(body(), 'GetBodyBox')]
P(u'包围盒 %s' % bx)
P(u'倒角前 %s' % before)
# 另外两根轴(外轮廓角要在这两根轴上都处于极值)
oth = [i for i in range(3) if i != AI]
def at_corner(p):
u"""该点在另外两根轴上是否都落在包围盒边界(即外轮廓的角)"""
for i in oth:
if abs(p[i] - bx[i]) > 0.01 and abs(p[i] - bx[i + 3]) > 0.01:
return False
return True
cand = []
for e in prop(body(), 'GetEdges'):
E = D(e)
try:
p0 = [round(v * 1000, 3) for v in prop(D(prop(E, 'GetStartVertex')), 'GetPoint')]
p1 = [round(v * 1000, 3) for v in prop(D(prop(E, 'GetEndVertex')), 'GetPoint')]
except Exception:
continue
same = [i for i in range(3) if abs(p0[i] - p1[i]) < 1e-6]
if len(same) != 2 or AI in same:
continue
if abs(p0[AI] - p1[AI]) < 1e-6:
continue
if not (at_corner(p0) and at_corner(p1)):
continue # ⇐ 关键:排除缺口/台阶产生的内部边
cand.append((E, p0))
P(u'外轮廓上沿 %s 的直边 = %d 条(v1 不加这一层筛选时是 12 / 8 条,含做不出的内角)'
% (AXIS, len(cand)))
for _, p0 in cand:
P(u' 角 (%.2f, %.2f)' % (p0[oth[0]], p0[oth[1]]))
if not cand:
P(u'⛔ 没有可倒的外轮廓边'); sys.exit(4)
done, failed = 0, []
for idx, (E, p0) in enumerate(cand):
ok = False
for r in RETRY:
part.ClearSelection2(True)
E.Select4(False, EMPTY)
if part.SelectionManager.GetSelectedObjectCount2(-1) != 1:
break
try:
feat = fm.FeatureFillet3(195, M(r), 0, 0, 0, 0, 0,
None, None, None, None, None, None, None)
except Exception as ex:
feat = None
if feat:
P(u' 边%d 角(%.2f, %.2f) → R%.1f ✅' % (idx, p0[oth[0]], p0[oth[1]], r))
done += 1
ok = True
break
if not ok:
P(u' 边%d 角(%.2f, %.2f) → 四档半径都做不出 ⛔' % (idx, p0[oth[0]], p0[oth[1]]))
failed.append(idx)
after = cyl_hist()
P(u'\n倒角后 %s' % after)
P(u'成功 %d / %d 条%s' % (done, len(cand), (u',失败 %s' % failed) if failed else u''))
if done:
e1 = wc.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0)
w1 = wc.VARIANT(pythoncom.VT_BYREF | pythoncom.VT_I4, 0)
part.Extension.SaveAs3(PATH, 0, 1, EMPTY, EMPTY, e1, w1)
P(u'存盘 err=%s %.0f KB' % (e1.value, os.path.getsize(PATH) / 1024.0))
else:
P(u'⛔ 一条都没倒成,不存盘')
P(u'⚠ 零件保持打开')