- 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.
193 lines
8.0 KiB
Python
193 lines
8.0 KiB
Python
from types import SimpleNamespace
|
|
from unittest.mock import patch
|
|
from tests.support.blender import BlenderTestCase
|
|
|
|
|
|
class AddonRegistrationTest(BlenderTestCase):
|
|
def test_import_does_not_add_addon_directories_to_sys_path(self):
|
|
self.assertEqual(self.paths_after_import, self.paths_before_import)
|
|
|
|
|
|
def test_operator_properties_use_plain_names(self):
|
|
operators = self.anyimage.operators
|
|
for operator in operators.CLASSES:
|
|
for name in getattr(operator, "__annotations__", {}):
|
|
self.assertFalse(name.startswith("anyimage_"), (operator, name))
|
|
|
|
|
|
def test_mesh_operators_are_registered_and_ordered(self):
|
|
from anyimage.operators.convert_to_panorama import ConvertToPanorama, GeneratePanorama
|
|
|
|
self.assertIn(ConvertToPanorama, self.anyimage.CLASSES)
|
|
self.assertIn(GeneratePanorama, self.anyimage.CLASSES)
|
|
for generator in (GeneratePanorama, self.convert_to_plane.GenerateDepthPlane):
|
|
self.assertIn(generator, self.anyimage.CLASSES)
|
|
for name in ("source_object_name", "source_identity", "image_identity"):
|
|
self.assertIn(name, generator.__annotations__)
|
|
self.assertIn("HIDDEN", generator.__annotations__[name]["options"])
|
|
self.assertLess(self.anyimage.CLASSES.index(GeneratePanorama), self.anyimage.CLASSES.index(ConvertToPanorama))
|
|
operator = self.convert_to_plane.ConvertToPlane
|
|
self.assertIn(operator, self.anyimage.CLASSES)
|
|
self.assertIn(self.convert_to_plane.ConvertToDepthPlane, self.anyimage.CLASSES)
|
|
self.assertIn(self.convert_to_plane.ConvertToReliefPlane, self.anyimage.CLASSES)
|
|
self.assertIn(self.cutout_main.CutoutSelectionToShape, self.anyimage.CLASSES)
|
|
self.assertEqual(operator.bl_idname, "anyimage.convert_to_plane")
|
|
self.assertIn("REGISTER", operator.bl_options)
|
|
self.assertIn("UNDO", operator.bl_options)
|
|
|
|
image_empty = SimpleNamespace(
|
|
type="EMPTY",
|
|
empty_display_type="IMAGE",
|
|
data=object(),
|
|
)
|
|
image_plane = SimpleNamespace(
|
|
type="MESH",
|
|
modifiers=[
|
|
SimpleNamespace(node_group=SimpleNamespace(name="O Image Plane"))
|
|
],
|
|
)
|
|
plain_mesh = SimpleNamespace(type="MESH", modifiers=[])
|
|
self.assertTrue(operator.poll(SimpleNamespace(object=image_empty)))
|
|
self.assertFalse(operator.poll(SimpleNamespace(object=image_plane)))
|
|
self.assertFalse(operator.poll(SimpleNamespace(object=plain_mesh)))
|
|
|
|
|
|
def test_image_edit_tools_and_operators_are_registered(self):
|
|
operator = self.edit_mask.EditImageAlpha
|
|
frame_operator = self.image_frame.FrameImages
|
|
self.assertIn(operator, self.anyimage.CLASSES)
|
|
self.assertIn(frame_operator, self.anyimage.CLASSES)
|
|
self.assertIn(
|
|
self.rectify.RectifyImagePerspective,
|
|
self.anyimage.CLASSES,
|
|
)
|
|
mask_tool = self.edit_mask.MaskTool
|
|
self.assertNotIn(mask_tool, self.anyimage.CLASSES)
|
|
self.assertEqual(mask_tool.bl_operator, operator.bl_idname)
|
|
self.assertEqual(operator.bl_options, {"UNDO"})
|
|
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_operator.bl_options, {"UNDO"})
|
|
rectify_tool = self.rectify.RectifyTool
|
|
self.assertNotIn(rectify_tool, self.anyimage.CLASSES)
|
|
self.assertEqual(
|
|
rectify_tool.bl_operator,
|
|
self.rectify.RectifyImagePerspective.bl_idname,
|
|
)
|
|
|
|
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",
|
|
mask_radius=25,
|
|
)
|
|
context = SimpleNamespace(
|
|
scene=SimpleNamespace(anyimage_settings=settings_value)
|
|
)
|
|
drawn_settings = []
|
|
layout = SimpleNamespace(
|
|
prop=lambda _settings, name, **_options: drawn_settings.append(name)
|
|
)
|
|
mask_tool.draw_settings(context, layout, None)
|
|
self.assertEqual(drawn_settings, ["mask_gesture", "mask_mode"])
|
|
settings_value.mask_gesture = "BRUSH"
|
|
drawn_settings.clear()
|
|
mask_tool.draw_settings(context, layout, None)
|
|
self.assertEqual(
|
|
drawn_settings,
|
|
["mask_gesture", "mask_mode", "mask_radius"],
|
|
)
|
|
settings_value.mask_gesture = "POLYLINE"
|
|
drawn_settings.clear()
|
|
mask_tool.draw_settings(context, layout, None)
|
|
self.assertEqual(drawn_settings, ["mask_gesture", "mask_mode"])
|
|
|
|
|
|
def test_registration_and_reverse_cleanup_preserve_other_addons(self):
|
|
self.assertIn(
|
|
self.anyimage.clipboard_image.PasteClipboardImage,
|
|
self.anyimage.CLASSES,
|
|
)
|
|
self.assertIn(
|
|
self.anyimage.clipboard_image.TrackNativeCopy,
|
|
self.anyimage.CLASSES,
|
|
)
|
|
existing_context_draw = object()
|
|
existing_outliner_draw = object()
|
|
existing_node_draw = object()
|
|
self.context_menu_draws.append(existing_context_draw)
|
|
self.outliner_menu_draws.append(existing_outliner_draw)
|
|
self.node_menu_draws.append(existing_node_draw)
|
|
|
|
self.anyimage.register()
|
|
system_classes = self.anyimage.runtime.operator_classes()
|
|
|
|
self.assertTrue(set(system_classes) <= set(self.registered))
|
|
self.assertEqual(
|
|
self.registered_tools,
|
|
[
|
|
(
|
|
self.image_frame.FrameTool,
|
|
{
|
|
"group": True,
|
|
"separator": True,
|
|
},
|
|
),
|
|
(
|
|
self.edit_mask.MaskTool,
|
|
{
|
|
"after": {self.image_frame.FrameTool.bl_idname},
|
|
"separator": False,
|
|
},
|
|
),
|
|
(
|
|
self.rectify.RectifyTool,
|
|
{
|
|
"after": {self.edit_mask.MaskTool.bl_idname},
|
|
"separator": False,
|
|
},
|
|
),
|
|
(
|
|
self.anyimage.CutoutTool,
|
|
{},
|
|
),
|
|
],
|
|
)
|
|
self.assertEqual(len(self.status_bar_draws), 1)
|
|
self.assertEqual(self.status_bar_draws[0]._owner, "anyimage")
|
|
self.assertIs(
|
|
self.context_menu_draws[0],
|
|
self.anyimage.draw_image_context_menu,
|
|
)
|
|
self.assertIs(
|
|
self.outliner_menu_draws[0],
|
|
self.anyimage.draw_image_context_menu,
|
|
)
|
|
|
|
self.assertEqual(self.node_menu_draws, [self.anyimage.draw_texture_node_context_menu, existing_node_draw])
|
|
self.assertIn(self.anyimage.AnyImageTextureNodeMenu, self.registered)
|
|
|
|
unregister_order = []
|
|
original_unregister = self.fake_bpy.utils.unregister_class
|
|
def unregister(cls):
|
|
unregister_order.append(cls)
|
|
original_unregister(cls)
|
|
with patch.object(self.fake_bpy.utils, "unregister_class", side_effect=unregister):
|
|
self.anyimage.unregister()
|
|
self.assertEqual(
|
|
[cls for cls in unregister_order if cls in self.anyimage.CLASSES],
|
|
list(reversed(self.anyimage.CLASSES)),
|
|
)
|
|
self.assertEqual(self.registered, [])
|
|
self.assertEqual(self.registered_tools, [])
|
|
self.assertEqual(self.status_bar_draws, [])
|
|
self.assertEqual(self.context_menu_draws, [existing_context_draw])
|
|
self.assertEqual(self.outliner_menu_draws, [existing_outliner_draw])
|
|
self.assertEqual(self.node_menu_draws, [existing_node_draw])
|
|
self.anyimage.register()
|
|
self.assertEqual(self.node_menu_draws, [self.anyimage.draw_texture_node_context_menu, existing_node_draw])
|
|
self.anyimage.unregister()
|
|
self.assertEqual(self.node_menu_draws, [existing_node_draw])
|