feat(cutout): implement boundary padding for cutout textures
- Introduced a new `Boundary Padding` preference in the Cutout Tool with a default of 2 px and a configurable range of 1-4 px. - Updated the Cutout client to establish inward mapping from the final Mask, sampling in a fixed direction and processing independent material RGBA and Depth. - Enhanced the handling of RGBA and Depth during cutout operations, ensuring proper mapping and preservation of source data. - Added tests to validate the new boundary padding functionality and its integration with existing cutout processes. feat(geometry): remove overall mesh smoothing from geometry nodes - Removed the `Smooth` input from several geometry node groups to eliminate unnecessary overall mesh smoothing. - Retained `Smooth Weight` for depth nodes while removing it from Cutout and Relief Plane nodes. - Ensured that boundary, normal, and fill smoothing behaviors remain intact and consistent. - Updated tests and documentation to reflect the changes in node interfaces and behaviors. feat(normals): scale tangent normals with depth - Reused the existing `o_normal_reduction` attribute to adjust Tangent Normal strength based on the geometry's `Depth Scale`. - Ensured that Tangent Normal strength is consistent with the geometry depth, preserving full strength for materials that do not provide the attribute. - Updated tests to verify the correct behavior of normal strength adjustments in both Tangent and Object Space.
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
import importlib
|
||||
import sys
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
from tests.support.blender import BlenderTestCase
|
||||
@@ -17,7 +15,7 @@ class AddonRegistrationTest(BlenderTestCase):
|
||||
self.assertFalse(name.startswith("anyimage_"), (operator, name))
|
||||
|
||||
|
||||
def test_mesh_operators_and_tools_are_registered(self):
|
||||
def test_mesh_operators_are_registered_and_ordered(self):
|
||||
from anyimage.operators.convert_to_panorama import ConvertToPanorama, GeneratePanorama
|
||||
|
||||
self.assertIn(ConvertToPanorama, self.anyimage.CLASSES)
|
||||
@@ -36,19 +34,6 @@ class AddonRegistrationTest(BlenderTestCase):
|
||||
self.assertEqual(operator.bl_idname, "anyimage.convert_to_plane")
|
||||
self.assertIn("REGISTER", operator.bl_options)
|
||||
self.assertIn("UNDO", operator.bl_options)
|
||||
self.assertEqual(operator.__annotations__["mesh_detail"]["default"], 0)
|
||||
self.assertEqual(
|
||||
self.convert_to_plane.ConvertToDepthPlane.__annotations__["mesh_detail"][
|
||||
"default"
|
||||
],
|
||||
6,
|
||||
)
|
||||
self.assertEqual(
|
||||
self.convert_to_plane.GenerateDepthPlane.__annotations__["mesh_detail"][
|
||||
"default"
|
||||
],
|
||||
6,
|
||||
)
|
||||
|
||||
image_empty = SimpleNamespace(
|
||||
type="EMPTY",
|
||||
@@ -80,36 +65,19 @@ class AddonRegistrationTest(BlenderTestCase):
|
||||
self.assertNotIn(mask_tool, self.anyimage.CLASSES)
|
||||
self.assertEqual(mask_tool.bl_operator, operator.bl_idname)
|
||||
self.assertEqual(operator.bl_options, {"UNDO"})
|
||||
self.assertEqual(mask_tool.bl_icon, "ops.sculpt.lasso_mask")
|
||||
self.assertEqual(mask_tool.bl_cursor, "PAINT_CROSS")
|
||||
self.assertEqual(
|
||||
mask_tool.bl_keymap[0][1]["value"],
|
||||
"PRESS",
|
||||
)
|
||||
self.assertEqual(
|
||||
mask_tool.bl_keymap[1][1],
|
||||
{"type": "LEFTMOUSE", "value": "CLICK", "shift": True},
|
||||
)
|
||||
frame_tool = self.image_frame.FrameTool
|
||||
self.assertNotIn(frame_tool, self.anyimage.CLASSES)
|
||||
self.assertEqual(frame_tool.bl_operator, frame_operator.bl_idname)
|
||||
self.assertEqual(frame_tool.bl_icon, "ops.sculpt.border_mask")
|
||||
self.assertEqual(frame_operator.bl_options, {"UNDO"})
|
||||
self.assertEqual(frame_tool.bl_keymap[0][1]["value"], "CLICK_DRAG")
|
||||
self.assertIsNone(frame_tool.bl_keymap[0][2])
|
||||
rectify_tool = self.rectify.RectifyTool
|
||||
self.assertNotIn(rectify_tool, self.anyimage.CLASSES)
|
||||
self.assertEqual(
|
||||
[entry[0] for entry in frame_tool.bl_keymap[1:]],
|
||||
["view3d.select", "view3d.select"],
|
||||
rectify_tool.bl_operator,
|
||||
self.rectify.RectifyImagePerspective.bl_idname,
|
||||
)
|
||||
self.assertEqual(
|
||||
frame_tool.bl_keymap[1][2],
|
||||
{"properties": [("deselect_all", True)]},
|
||||
)
|
||||
self.assertEqual(
|
||||
frame_tool.bl_keymap[2][2],
|
||||
{"properties": [("toggle", True)]},
|
||||
)
|
||||
self.assertIn("draw_settings", mask_tool.__dict__)
|
||||
|
||||
def test_mask_tool_settings_follow_the_active_gesture(self):
|
||||
mask_tool = self.edit_mask.MaskTool
|
||||
settings_value = SimpleNamespace(
|
||||
mask_gesture="LASSO",
|
||||
mask_mode="SET",
|
||||
@@ -135,130 +103,6 @@ class AddonRegistrationTest(BlenderTestCase):
|
||||
drawn_settings.clear()
|
||||
mask_tool.draw_settings(context, layout, None)
|
||||
self.assertEqual(drawn_settings, ["mask_gesture", "mask_mode"])
|
||||
cutout_tool = importlib.import_module(
|
||||
"anyimage.operators.cutout_tool"
|
||||
).CutoutTool
|
||||
self.assertEqual(
|
||||
cutout_tool.bl_icon,
|
||||
"ops.mesh.primitive_sphere_add_gizmo",
|
||||
)
|
||||
self.assertEqual(cutout_tool.bl_keymap[0][1]["value"], "PRESS")
|
||||
self.assertEqual(
|
||||
[entry[0] for entry in cutout_tool.bl_keymap[1:]],
|
||||
["view3d.select"],
|
||||
)
|
||||
self.assertEqual(
|
||||
cutout_tool.bl_keymap[1][2],
|
||||
{"properties": [("toggle", True)]},
|
||||
)
|
||||
cutout_module = sys.modules[cutout_tool.__module__]
|
||||
self.assertIn(
|
||||
"selection_path_json",
|
||||
cutout_module.CutoutSelectionToShape.__annotations__,
|
||||
)
|
||||
drawn_settings.clear()
|
||||
with (
|
||||
patch.object(
|
||||
cutout_module,
|
||||
"draw_ai_setup",
|
||||
return_value={"ready": True},
|
||||
) as setup,
|
||||
patch.object(
|
||||
cutout_module,
|
||||
"draw_ai_property",
|
||||
side_effect=lambda _layout, _settings, name, _status: (
|
||||
drawn_settings.append(name)
|
||||
),
|
||||
) as draw,
|
||||
):
|
||||
cutout_tool.draw_settings(context, layout, None)
|
||||
setup.assert_called_once_with(layout)
|
||||
self.assertEqual(draw.call_count, 1)
|
||||
self.assertEqual(
|
||||
drawn_settings,
|
||||
[
|
||||
"cutout_gesture",
|
||||
"cutout_edge_length",
|
||||
"cutout_fine_outline",
|
||||
"cutout_alpha_threshold",
|
||||
"cutout_generate_normal",
|
||||
],
|
||||
)
|
||||
self.assertEqual(operator.__annotations__["gesture"]["default"], "LASSO")
|
||||
self.assertEqual(
|
||||
[item[:2] for item in operator.__annotations__["gesture"]["items"]],
|
||||
[
|
||||
("LASSO", "Lasso"),
|
||||
("BRUSH", "Brush"),
|
||||
("POLYLINE", "Polyline"),
|
||||
],
|
||||
)
|
||||
self.assertEqual(operator.__annotations__["mode"]["default"], "SET")
|
||||
self.assertEqual(operator.__annotations__["radius"]["default"], 25)
|
||||
settings = self.anyimage.properties.AnyImageSettings.__annotations__
|
||||
self.assertEqual(settings["mask_gesture"]["default"], "LASSO")
|
||||
self.assertEqual(settings["cutout_gesture"]["default"], "LASSO")
|
||||
self.assertEqual(
|
||||
[item[0] for item in settings["cutout_gesture"]["items"]],
|
||||
["LASSO", "POLYLINE"],
|
||||
)
|
||||
self.assertEqual(
|
||||
cutout_module.SelectCutoutSelection.__annotations__["gesture"]["default"],
|
||||
"LASSO",
|
||||
)
|
||||
self.assertEqual(
|
||||
[(item[0], item[1], item[4]) for item in settings["mask_mode"]["items"]],
|
||||
[("SET", "Set", 0), ("EXTEND", "Extend", 1), ("SUBTRACT", "Subtract", 2)],
|
||||
)
|
||||
self.assertEqual(settings["mask_mode"]["default"], "SET")
|
||||
self.assertEqual(settings["mask_radius"]["default"], 25)
|
||||
self.assertEqual(
|
||||
{name for name in settings if name.startswith("mask_")},
|
||||
{"mask_gesture", "mask_mode", "mask_radius"},
|
||||
)
|
||||
self.assertEqual(
|
||||
set(operator.__annotations__),
|
||||
{
|
||||
"source_object_name",
|
||||
"source_matrix_data",
|
||||
"gesture",
|
||||
"mode",
|
||||
"radius",
|
||||
},
|
||||
)
|
||||
self.assertEqual(settings["cutout_edge_length"]["default"], 0.1)
|
||||
self.assertFalse(settings["cutout_fine_outline"]["default"])
|
||||
self.assertFalse(cutout_module.CutoutSelectionToShape.__annotations__["fine_outline"]["default"])
|
||||
self.assertEqual(settings["cutout_alpha_threshold"]["default"], 0.9)
|
||||
self.assertEqual(settings["cutout_alpha_threshold"]["min"], 0.0)
|
||||
self.assertEqual(settings["cutout_alpha_threshold"]["max"], 1.0)
|
||||
self.assertEqual(settings["cutout_alpha_threshold"]["soft_min"], 0.1)
|
||||
self.assertEqual(settings["cutout_alpha_threshold"]["soft_max"], 0.9)
|
||||
self.assertEqual(settings["cutout_alpha_threshold"]["subtype"], "FACTOR")
|
||||
self.assertEqual(settings["cutout_edge_length"]["unit"], "LENGTH")
|
||||
self.assertFalse(settings["cutout_generate_normal"]["default"])
|
||||
self.assertEqual(
|
||||
settings["cutout_edge_length"]["name"], "Edge Length"
|
||||
)
|
||||
self.assertEqual(
|
||||
settings["cutout_generate_normal"]["name"],
|
||||
"Normal Map",
|
||||
)
|
||||
|
||||
|
||||
def test_rectify_tool_registration_and_properties(self):
|
||||
operator = self.rectify.RectifyImagePerspective
|
||||
tool = self.rectify.RectifyTool
|
||||
self.assertIn(operator, self.anyimage.CLASSES)
|
||||
self.assertNotIn(tool, self.anyimage.CLASSES)
|
||||
self.assertEqual(tool.bl_operator, operator.bl_idname)
|
||||
self.assertEqual(tool.bl_icon, "ops.sculpt.line_project")
|
||||
self.assertEqual(tool.bl_keymap[0][1]["value"], "PRESS")
|
||||
self.assertEqual(
|
||||
tool.bl_keymap[1][1],
|
||||
{"type": "LEFTMOUSE", "value": "CLICK", "shift": True},
|
||||
)
|
||||
self.assertIn("aspect_ratio", operator.__annotations__)
|
||||
|
||||
|
||||
def test_registration_and_reverse_cleanup_preserve_other_addons(self):
|
||||
@@ -277,12 +121,6 @@ class AddonRegistrationTest(BlenderTestCase):
|
||||
self.outliner_menu_draws.append(existing_outliner_draw)
|
||||
self.node_menu_draws.append(existing_node_draw)
|
||||
|
||||
settings = self.anyimage.properties.AnyImageSettings.__annotations__
|
||||
self.assertEqual(set(settings), {
|
||||
"cutout_gesture", "cutout_alpha_threshold", "cutout_edge_length", "cutout_fine_outline",
|
||||
"cutout_generate_normal", "mask_gesture",
|
||||
"mask_mode", "mask_radius",
|
||||
})
|
||||
self.anyimage.register()
|
||||
system_classes = self.anyimage.runtime.operator_classes()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user