ModelTranslator: QR tolerance soft-gate + precollapse target policy
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
eb92ecacfa
commit
bb6f32b1c1
@@ -4,6 +4,9 @@ import glob
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
QR_TIMEOUT = 120.0 # 引擎轮询超时(秒)
|
QR_TIMEOUT = 120.0 # 引擎轮询超时(秒)
|
||||||
|
QR_INPUT_CAP = 50000 # quad 后端 QR 前预 collapse 的中等密度目标面数
|
||||||
|
QR_HOLE_MAX_SIDES = 6 # 补洞上限边数:只补 <=6 边的薄部位小洞
|
||||||
|
QR_CATASTROPHIC_TOL = 12 # 软水密门容忍:补洞后仍多出超此数的开边/非流形才判灾难性
|
||||||
|
|
||||||
|
|
||||||
def find_qr_engine():
|
def find_qr_engine():
|
||||||
@@ -65,8 +68,14 @@ def parse_progress(text):
|
|||||||
|
|
||||||
|
|
||||||
def topology_acceptable(src_boundary, src_nonmanifold,
|
def topology_acceptable(src_boundary, src_nonmanifold,
|
||||||
out_boundary, out_nonmanifold):
|
out_boundary, out_nonmanifold, tol=0):
|
||||||
"""QR 输出拓扑是否可接受:不比输入引入更多开边(孔洞)/非流形边。
|
"""QR 输出拓扑是否可接受:不比输入多出超过 tol 的开边(孔洞)/非流形边。
|
||||||
QR 引擎在激进减面下会非确定性地打出孔洞——封闭输入(0/0)时要求输出也 0/0,
|
tol=0(默认)为零容忍严格门;quad 后端补小洞后用 tol=QR_CATASTROPHIC_TOL 只挡灾难性破损。"""
|
||||||
输入本身开放(壳)时按其基线放行;否则判破损,交上层回退 collapse。"""
|
return (out_boundary <= src_boundary + tol
|
||||||
return out_boundary <= src_boundary and out_nonmanifold <= src_nonmanifold
|
and out_nonmanifold <= src_nonmanifold + tol)
|
||||||
|
|
||||||
|
|
||||||
|
def precollapse_target(face_count, cap=QR_INPUT_CAP):
|
||||||
|
"""QR 前预 collapse 目标:面数 > cap 返回 cap(先减到中等密度),否则 None(跳过)。
|
||||||
|
高密度网格直接 QR 必破洞,先 collapse 到中等密度给 QR 一个稳定输入。"""
|
||||||
|
return cap if face_count > cap else None
|
||||||
|
|||||||
@@ -141,5 +141,38 @@ class TestTopologyAcceptable(unittest.TestCase):
|
|||||||
self.assertTrue(qb.topology_acceptable(10, 2, 8, 1))
|
self.assertTrue(qb.topology_acceptable(10, 2, 8, 1))
|
||||||
|
|
||||||
|
|
||||||
|
class TestTopologyTolerance(unittest.TestCase):
|
||||||
|
def test_tol_allows_small_increase(self):
|
||||||
|
# 封闭输入 + QR 多出 3 开边,容忍 12 内 -> 接受
|
||||||
|
self.assertTrue(qb.topology_acceptable(0, 0, 3, 0, tol=12))
|
||||||
|
|
||||||
|
def test_tol_rejects_large_increase(self):
|
||||||
|
self.assertFalse(qb.topology_acceptable(0, 0, 13, 0, tol=12))
|
||||||
|
|
||||||
|
def test_tol_applies_to_nonmanifold(self):
|
||||||
|
self.assertTrue(qb.topology_acceptable(0, 0, 0, 12, tol=12))
|
||||||
|
self.assertFalse(qb.topology_acceptable(0, 0, 0, 13, tol=12))
|
||||||
|
|
||||||
|
def test_default_tol_zero_strict(self):
|
||||||
|
# 默认 tol=0:现有严格语义不变
|
||||||
|
self.assertFalse(qb.topology_acceptable(0, 0, 1, 0))
|
||||||
|
self.assertTrue(qb.topology_acceptable(0, 0, 0, 0))
|
||||||
|
|
||||||
|
|
||||||
|
class TestPrecollapseTarget(unittest.TestCase):
|
||||||
|
def test_above_cap_returns_cap(self):
|
||||||
|
self.assertEqual(qb.precollapse_target(1000000, 50000), 50000)
|
||||||
|
|
||||||
|
def test_below_cap_returns_none(self):
|
||||||
|
self.assertIsNone(qb.precollapse_target(40000, 50000))
|
||||||
|
|
||||||
|
def test_at_cap_returns_none(self):
|
||||||
|
self.assertIsNone(qb.precollapse_target(50000, 50000))
|
||||||
|
|
||||||
|
def test_default_cap(self):
|
||||||
|
self.assertEqual(qb.precollapse_target(60000), qb.QR_INPUT_CAP)
|
||||||
|
self.assertIsNone(qb.precollapse_target(40000))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user