feat(body_mesh): Add body mesh separator module with edge dissolution

- Add body_mesh submodule with VRM_OT_separate_non_skin_faces operator
- Add VRM_PT_body_mesh_separator panel for UI integration
- Add utility functions for mesh operations and edge dissolution logic
- Add comprehensive edge dissolution configuration files for all body parts (head, torso, arms, legs, hands) with UV coordinates
- Add test utilities for validating mesh separation operations
- Add .gitignore to exclude Python cache files and test artifacts
- Register body_mesh module in editor package initialization
- Update registration.py to include new body mesh components
This commit is contained in:
2026-01-01 22:20:31 +08:00
parent 091ad6a49a
commit d70b36fd75
23 changed files with 87935 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
# SPDX-License-Identifier: MIT OR GPL-3.0-or-later
"""
Property-based tests for body_mesh utility functions.
Feature: vrm-body-mesh-separator
"""
from hypothesis import given, settings
from hypothesis import strategies as st
from .utils import generate_unique_name, is_skin_material
class TestIsSkinMaterial:
"""
Property 2: 材质分类正确性
*For any* 材质名称字符串,`is_skin_material`函数应该返回True当且仅当该名称包含"SKIN"子字符串(不区分大小写)。
**Validates: Requirements 2.2, 2.3**
"""
@given(st.text())
@settings(max_examples=100)
def test_skin_material_classification_property(self, material_name: str) -> None:
"""
Feature: vrm-body-mesh-separator, Property 2: 材质分类正确性
For any material name string, is_skin_material returns True
if and only if the name contains "SKIN" (case-insensitive).
"""
result = is_skin_material(material_name)
expected = "SKIN" in material_name.upper()
assert result == expected, (
f"is_skin_material('{material_name}') returned {result}, "
f"expected {expected}"
)
class TestGenerateUniqueName:
"""
Property 6: 唯一命名保证
*For any* 基础名称和已存在名称集合,`generate_unique_name`返回的名称不应该在已存在名称集合中。
**Validates: Requirements 4.2**
"""
@given(
st.text(min_size=1, max_size=50).filter(lambda x: x.strip()),
st.sets(st.text(min_size=1, max_size=50), max_size=20),
)
@settings(max_examples=100)
def test_unique_name_generation_property(
self, base_name: str, existing_names: set[str]
) -> None:
"""
Feature: vrm-body-mesh-separator, Property 6: 唯一命名保证
For any base name and set of existing names, generate_unique_name
returns a name that is not in the existing names set.
"""
result = generate_unique_name(base_name, existing_names)
assert result not in existing_names, (
f"generate_unique_name('{base_name}', {existing_names}) "
f"returned '{result}' which is in existing_names"
)