From 788122b271db0d26e4da2a8370c39ad3af07d8f2 Mon Sep 17 00:00:00 2001 From: ud18010 Date: Tue, 21 Jul 2026 11:13:23 +0800 Subject: [PATCH] =?UTF-8?q?ModelTranslator:=20=E6=96=B0=E5=A2=9E=20pick=5F?= =?UTF-8?q?best=5Fcandidate=E2=80=94=E2=80=94=E8=BF=87=E9=97=A8=E4=B8=94?= =?UTF-8?q?=E5=B2=9B=E6=95=B0=E6=9C=80=E5=B0=91=E8=80=85=E6=8B=A9=E4=BC=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- Tools/ModelTranslator/bl_decimate.py | 10 +++++++ .../ModelTranslator/tests/test_bl_decimate.py | 29 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/Tools/ModelTranslator/bl_decimate.py b/Tools/ModelTranslator/bl_decimate.py index 2838df92..d0f0fc69 100644 --- a/Tools/ModelTranslator/bl_decimate.py +++ b/Tools/ModelTranslator/bl_decimate.py @@ -38,6 +38,16 @@ def uvpm_mode_label(base, applied): return base + "+uvpm" if applied else base +def pick_best_candidate(candidates): + """从候选 UV 指标 dict 列表选过质量门且岛数最少者;无过门候选返回 None。 + 每个 candidate 至少含 flipped/overlap/islands。""" + passing = [c for c in candidates + if uv_gate_ok(c["flipped"], c["overlap"])] + if not passing: + return None + return min(passing, key=lambda c: c["islands"]) + + def count_islands(n_faces, adjacent_pairs): """union-find 数 UV 岛:n_faces 个面,adjacent_pairs 为 UV 连续的面索引对。""" parent = list(range(n_faces)) diff --git a/Tools/ModelTranslator/tests/test_bl_decimate.py b/Tools/ModelTranslator/tests/test_bl_decimate.py index daa24ba8..4edc217a 100644 --- a/Tools/ModelTranslator/tests/test_bl_decimate.py +++ b/Tools/ModelTranslator/tests/test_bl_decimate.py @@ -103,5 +103,34 @@ class TestUvpmModeLabel(unittest.TestCase): self.assertEqual(bd.uvpm_mode_label("smart_fallback", False), "smart_fallback") +class TestPickBestCandidate(unittest.TestCase): + def _c(self, flipped, overlap, islands, angle): + return {"flipped": flipped, "overlap": overlap, + "islands": islands, "angle": angle} + + def test_none_when_empty(self): + self.assertIsNone(bd.pick_best_candidate([])) + + def test_none_when_no_candidate_passes_gate(self): + cands = [self._c(0.30, 0.50, 100, 66), self._c(0.20, 0.40, 200, 45)] + self.assertIsNone(bd.pick_best_candidate(cands)) + + def test_picks_only_passing(self): + cands = [self._c(0.30, 0.50, 50, 66), self._c(0.00, 0.00, 300, 45)] + best = bd.pick_best_candidate(cands) + self.assertEqual(best["angle"], 45) + + def test_picks_fewest_islands_among_passing(self): + cands = [self._c(0.00, 0.00, 120, 55), self._c(0.01, 0.02, 90, 45), + self._c(0.00, 0.00, 300, 35)] + best = bd.pick_best_candidate(cands) + self.assertEqual(best["islands"], 90) + + def test_overlap_none_treated_as_pass(self): + cands = [self._c(0.01, None, 42, 66)] + best = bd.pick_best_candidate(cands) + self.assertEqual(best["islands"], 42) + + if __name__ == "__main__": unittest.main()