Add tests and implement boundary smoothing and cutout functionality

- Introduced tests for boundary smoothing in `test_boundary_smoothing.py`, verifying that smoothing reduces stair-step artifacts while preserving boundary integrity.
- Added tests for cutout boundary behavior in `test_cutout_boundary.py`, ensuring proper handling of boundary triangles and normal blending.
- Created `test_temporary_attributes.py` to check the cleanup of temporary attributes across depth modifier branches, ensuring no warnings are raised.
- Implemented point culling functionality in `test_validity.py`, confirming that invalid points are removed without affecting adjacent geometry.
- Developed boundary smoothing logic in `boundary_smoothing.py`, allowing for depth smoothing while maintaining edge integrity.
- Added cutout boundary management in `cutout_boundary.py`, including functions for removing boundary triangles and tapering thickness profiles.
- Implemented validity checks in `validity.py` to ensure proper deletion of invalid vertices and associated wire edges.
This commit is contained in:
2026-09-11 20:37:20 +08:00
parent e8129fe495
commit 31074bd511
85 changed files with 2501 additions and 1451 deletions

View File

@@ -1,4 +1,5 @@
import bpy
import pytest
from anyimage.common.object import (
modifier_input_identifier,
@@ -17,6 +18,46 @@ def evaluated_mesh(obj):
return evaluated, evaluated.to_mesh()
@pytest.mark.parametrize("source_count", [0, 4, 9])
def test_material_inheritance_removes_only_source_vertices(source_count):
from tools.nodes.common.material import inherit_material_slots
from tools.nodes.arrangement.arrange import arrange_nodes
bpy.ops.wm.read_factory_settings(use_empty=True)
mesh = bpy.data.meshes.new("Source")
mesh.from_pydata([(20 + i % 3, i // 3, 0) for i in range(source_count)], [],
[(0, 1, 3, 2)] if source_count else [])
materials = [bpy.data.materials.new(name) for name in ("First", "Second")]
for material in materials:
mesh.materials.append(material)
obj = bpy.data.objects.new("Material Source", mesh)
bpy.context.collection.objects.link(obj)
group = bpy.data.node_groups.new("Material Inheritance", "GeometryNodeTree")
group.interface.new_socket(name="Geometry", in_out="INPUT", socket_type="NodeSocketGeometry")
group.interface.new_socket(name="Geometry", in_out="OUTPUT", socket_type="NodeSocketGeometry")
source = group.nodes.new("NodeGroupInput")
grid = group.nodes.new("GeometryNodeMeshGrid")
grid.inputs["Vertices X"].default_value = 3
grid.inputs["Vertices Y"].default_value = 2
geometry = inherit_material_slots(group, grid.outputs["Mesh"], source.outputs["Geometry"])
group.links.new(geometry, group.nodes.new("NodeGroupOutput").inputs["Geometry"])
obj.modifiers.new("Material Inheritance", "NODES").node_group = group
for arranged in (False, True):
if arranged:
arrange_nodes(group)
obj.update_tag(refresh={"DATA"})
ev, result = evaluated_mesh(obj)
try:
assert len(result.vertices) == 6 and len(result.polygons) == 2
assert all(abs(v.co.x) <= 0.5 for v in result.vertices)
if source_count:
assert [material.original for material in result.materials if material] == materials
assert result.materials[0].original == materials[0]
assert all(face.material_index == 0 for face in result.polygons)
finally:
ev.to_mesh_clear()
def test_saved_image_plane_evaluation():
bpy.ops.wm.read_factory_settings(use_empty=True)
mesh = create_image_plane_mesh("Image Plane", (-1.0, 1.0, -0.5, 0.5))