Add soft min and max constraints for frame resolution and cutout edge length properties

This commit is contained in:
2026-09-13 11:22:20 +08:00
parent 18799e8828
commit 2a00ed0ee6
21 changed files with 3834 additions and 1293 deletions

View File

@@ -39,6 +39,7 @@ def test_cutout_depth_axis_is_adjusted_only_in_modifier(shape, gesture):
@ Matrix.Diagonal((1.5, 0.75, 2.0, 1.0))
)
bpy.context.collection.objects.link(source)
bpy.context.view_layer.update()
positive_x = _create_cutout_shape(
source,
@@ -56,11 +57,11 @@ def test_cutout_depth_axis_is_adjusted_only_in_modifier(shape, gesture):
for result in (positive_x, negative_z):
modifier = result.modifiers[0]
mode = object_data.modifier_input_identifier(modifier.node_group, "Mode")
assert modifier[mode] == (1 if gesture == "POLYLINE" else 0)
assert modifier_value(modifier, mode) == (1 if gesture == "POLYLINE" else 0)
thickness = object_data.modifier_input_identifier(
modifier.node_group, "Thickness", subtype="DISTANCE",
)
assert modifier[thickness] == (0.5 if shape == "SOLID" and gesture == "POLYLINE" else 0.0)
assert modifier_value(modifier, thickness) == (0.5 if shape == "SOLID" and gesture == "POLYLINE" else 0.0)
local = np.asarray([tuple(vertex.co) for vertex in result.data.vertices])
assert np.allclose(local[:, 2], 0.0, atol=1e-8)
assert np.ptp(local[:, 0]) > 0.0
@@ -70,7 +71,7 @@ def test_cutout_depth_axis_is_adjusted_only_in_modifier(shape, gesture):
assert np.allclose(positive_x.matrix_world, source.matrix_world)
modifier = positive_x.modifiers[0]
axis = object_data.modifier_input_identifier(modifier.node_group, "Depth Axis")
assert modifier[axis] == 0
assert modifier_value(modifier, axis) == 0
object_data.set_modifier_input(modifier, axis, 1)
positive_x.update_tag(refresh={"DATA"})
bpy.context.view_layer.update()
@@ -261,9 +262,9 @@ def test_depth_surface_protected_smooth_evaluates(gesture):
assert cutout_shape.BALLOON_ATTRIBUTE_NAME in result.data.attributes
modifier = result.modifiers[0]
assert modifier.node_group.name == cutout_shape.CUTOUT_NODE_GROUP_NAMES["DEPTH_SOLID"]
assert modifier[object_data.modifier_input_identifier(modifier.node_group, "Mode")] == (1 if gesture == "POLYLINE" else 0)
assert modifier[object_data.modifier_input_identifier(modifier.node_group, "Thickness", subtype="DISTANCE")] == 0.5
assert modifier[object_data.modifier_input_identifier(modifier.node_group, "Thickness", subtype="NONE")] == 1.0
assert modifier_value(modifier, object_data.modifier_input_identifier(modifier.node_group, "Mode")) == (1 if gesture == "POLYLINE" else 0)
assert modifier_value(modifier, object_data.modifier_input_identifier(modifier.node_group, "Thickness", subtype="DISTANCE")) == 0.5
assert modifier_value(modifier, object_data.modifier_input_identifier(modifier.node_group, "Thickness", subtype="NONE")) == 1.0
object_data.set_modifier_input(
modifier,
object_data.modifier_input_identifier(modifier.node_group, "Thickness", subtype="DISTANCE"),
@@ -274,14 +275,12 @@ def test_depth_surface_protected_smooth_evaluates(gesture):
cutout_geometry.world_plane_metric(source.matrix_world)[0],
)
assert np.isclose(
modifier[object_data.modifier_input_identifier(modifier.node_group, "Cleanup Threshold")],
modifier_value(modifier, object_data.modifier_input_identifier(modifier.node_group, "Cleanup Threshold")),
expected_cleanup,
)
assert np.isclose(modifier[object_data.modifier_input_identifier(modifier.node_group, "Smooth Weight")], 1.0)
assert np.isclose(modifier_value(modifier, object_data.modifier_input_identifier(modifier.node_group, "Smooth Weight")), 1.0)
assert (
modifier[
object_data.modifier_input_identifier(modifier.node_group, "Smooth")
]
modifier_value(modifier, object_data.modifier_input_identifier(modifier.node_group, "Smooth"))
== 0
)
for name, value in (("Mode", 1), ("Front Inflation", 0.0)):
@@ -290,7 +289,7 @@ def test_depth_surface_protected_smooth_evaluates(gesture):
modifier.node_group,
"Reference Depth",
)
assert modifier[reference_id] == 8.0
assert modifier_value(modifier, reference_id) == 8.0
object_data.set_modifier_input(
modifier,
object_data.modifier_input_identifier(modifier.node_group, "Smooth Weight"),
@@ -639,7 +638,7 @@ def test_depth_balloon_uses_metric_plane_inputs():
modifier.node_group,
"Depth Image",
)
assert isinstance(modifier[depth_image_id], bpy.types.Image)
assert isinstance(modifier_value(modifier, depth_image_id), bpy.types.Image)
expected_center = np.zeros(2, dtype=np.float64)
crop_center = Vector(
(
@@ -659,18 +658,18 @@ def test_depth_balloon_uses_metric_plane_inputs():
modifier.node_group,
"Reference Depth",
)
assert not np.isclose(modifier[depth_reference_id], 8.0)
assert not np.isclose(modifier_value(modifier, depth_reference_id), 8.0)
depth_direction_id = object_data.modifier_input_identifier(
modifier.node_group,
"Depth Direction",
)
direction = np.asarray(modifier[depth_direction_id], dtype=np.float64)
direction = np.asarray(modifier_value(modifier, depth_direction_id), dtype=np.float64)
assert not np.allclose(direction, (0.0, 0.0, 1.0))
uniform_scale_id = object_data.modifier_input_identifier(
modifier.node_group,
"Uniform Scale",
)
assert modifier[uniform_scale_id] > 0.0
assert modifier_value(modifier, uniform_scale_id) > 0.0
thickness_id = object_data.modifier_input_identifier(
modifier.node_group,
"Thickness",
@@ -679,7 +678,7 @@ def test_depth_balloon_uses_metric_plane_inputs():
modifier.node_group,
"Smooth Weight",
)
assert modifier[smooth_id] == 1.0
assert modifier_value(modifier, smooth_id) == 1.0
object_data.set_modifier_input(modifier, smooth_id, 0.0)
result.update_tag(refresh={"DATA"})
bpy.context.view_layer.update()
@@ -707,7 +706,7 @@ def test_depth_balloon_uses_metric_plane_inputs():
object_data.set_modifier_input(
modifier,
depth_reference_id,
modifier[depth_reference_id] + 0.2,
modifier_value(modifier, depth_reference_id) + 0.2,
)
result.update_tag(refresh={"DATA"})
bpy.context.view_layer.update()
@@ -729,7 +728,7 @@ def test_depth_balloon_uses_metric_plane_inputs():
object_data.set_modifier_input(
modifier,
depth_reference_id,
modifier[depth_reference_id] + 0.15,
modifier_value(modifier, depth_reference_id) + 0.15,
)
object_data.set_modifier_input(modifier, depth_direction_id, (0.2, 0.1, 1.0))
result.update_tag(refresh={"DATA"})
@@ -889,17 +888,15 @@ def test_local_cutout_builds_from_blender_image_pixels_without_a_job():
cutout_geometry.world_plane_metric(source.matrix_world)[0],
)
assert np.isclose(
modifier[object_data.modifier_input_identifier(modifier.node_group, "Cleanup Threshold")],
modifier_value(modifier, object_data.modifier_input_identifier(modifier.node_group, "Cleanup Threshold")),
expected_cleanup,
)
assert (
modifier[object_data.modifier_input_identifier(modifier.node_group, "Smooth Weight")]
modifier_value(modifier, object_data.modifier_input_identifier(modifier.node_group, "Smooth Weight"))
== 1.0
)
assert (
modifier[
object_data.modifier_input_identifier(modifier.node_group, "Smooth")
]
modifier_value(modifier, object_data.modifier_input_identifier(modifier.node_group, "Smooth"))
== 0
)
assert len(result.data.vertices) > 4
@@ -931,3 +928,15 @@ def test_local_cutout_builds_from_blender_image_pixels_without_a_job():
assert np.allclose(
copied[:, :, 3], np.flipud(rgba[top:bottom, left:right, 3])
)
def modifier_value(modifier, identifier):
"""Read modifier values through the runtime's socket API."""
inputs = object_data._modifier_input_slots(modifier)
if inputs is None:
return modifier[identifier]
slot = getattr(inputs, identifier)
prop = slot.bl_rna.properties["value"]
if prop.type == "ENUM":
return list(prop.enum_items.keys()).index(slot.value)
return slot.value