feat: Implement depth surface strip cleanup and organize tool CLI

- Added a proposal for depth surface strip cleanup, detailing the rationale and changes to the depth plane and cutout nodes.
- Created specifications for the new cleanup requirements, including scenarios for strip removal and boundary edge detection.
- Developed tasks for implementing shared strip detection logic and integrating new interfaces for depth nodes.
- Established a new CLI structure for organizing node groups and model commands, replacing old entry points with a more intuitive command structure.
- Introduced a prototype for panorama surface generation, including requirements for distance reconstruction and geometry node surfaces.
- Added comprehensive tests for depth strip cleanup functionality, ensuring correct behavior for various mesh topologies and edge cases.
This commit is contained in:
2026-09-10 00:53:43 +08:00
parent 1622afa7a2
commit 27ca25224a
42 changed files with 1970 additions and 70 deletions

View File

@@ -147,11 +147,14 @@ def test_coincident_independent_sheets_do_not_weld_together():
@pytest.mark.parametrize("resolution", [1, 2, 4])
def test_planar_depth_has_no_split_at_any_density(resolution):
def test_planar_depth_preserves_blocks_and_cleans_single_face_width(resolution):
obj, set_value = surface(step=0.0, resolution=resolution)
original, original_faces = evaluated(obj)
set_value("Split Threshold", 1.0)
coords, faces = evaluated(obj)
if resolution == 1:
assert len(coords) == 0 and not faces
return
assert len(coords) == len(obj.data.vertices)
assert len(faces) == len(obj.data.polygons)
np.testing.assert_allclose(coords, original, atol=1e-6)
@@ -219,8 +222,17 @@ def test_temporary_attributes_are_removed_and_user_attributes_survive(thickness,
result.to_mesh_clear()
def output_split_mesh(obj):
"""Inspect separation before boundary strip cleanup."""
group = obj.modifiers[0].node_group
split = next(node for node in group.nodes if node.bl_idname == "GeometryNodeSplitEdges")
output = next(node for node in group.nodes if node.bl_idname == "NodeGroupOutput")
group.links.new(split.outputs["Mesh"], output.inputs["Geometry"])
def test_zero_center_distance_does_not_split_or_produce_nan():
obj, set_value = surface(step=6.0)
output_split_mesh(obj)
mesh = bpy.data.meshes.new("Coincident face centers")
mesh.from_pydata(
[(0, 0, 0), (1, 0, 0), (0.5, 1, 0), (0.5, 1, 0)], [], [(0, 1, 2), (1, 0, 3)]
@@ -258,6 +270,12 @@ def test_triangle_corner_weights_do_not_open_sloped_side_walls():
@pytest.mark.parametrize("resolution", [1, 2, 4])
def test_linear_depth_slope_has_density_independent_selection(resolution):
obj, set_value = surface(step=0.0, resolution=resolution)
output_split_mesh(obj)
bm = bmesh.new()
bm.from_mesh(obj.data)
bmesh.ops.triangulate(bm, faces=list(bm.faces))
bm.to_mesh(obj.data)
bm.free()
image = bpy.data.images["Camera"]
pixels = np.array(image.pixels[:], np.float32).reshape(32, 64, 4)
pixels[..., 2] = 1 + 4 * pixels[..., 0]