feat: Enhance AI integration for Cutout, Image Box, and Image Lasso tools with unified settings and improved shape selection
This commit is contained in:
@@ -1433,13 +1433,23 @@ class BlenderAddonTest(unittest.TestCase):
|
||||
).CutoutTool
|
||||
cutout_module = sys.modules[cutout_tool.__module__]
|
||||
drawn_settings.clear()
|
||||
with patch.object(
|
||||
cutout_module,
|
||||
"draw_region_settings",
|
||||
side_effect=lambda _context, _layout, name: drawn_settings.append(name),
|
||||
) as draw:
|
||||
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)
|
||||
draw.assert_called_once_with(context, layout, "cutout_refine_region")
|
||||
setup.assert_called_once_with(layout)
|
||||
self.assertEqual(draw.call_count, 2)
|
||||
self.assertEqual(
|
||||
drawn_settings,
|
||||
[
|
||||
@@ -1481,6 +1491,83 @@ class BlenderAddonTest(unittest.TestCase):
|
||||
self.assertNotIn("image_extract_padding", settings)
|
||||
self.assertNotIn("image_extract_remove_background", settings)
|
||||
|
||||
def test_image_area_tool_settings_gate_ai_options(self):
|
||||
class Row:
|
||||
def __init__(self, events):
|
||||
self.events = events
|
||||
self.enabled = True
|
||||
|
||||
def prop(self, _owner, name):
|
||||
self.events.append(("prop", name, self.enabled))
|
||||
|
||||
class Layout:
|
||||
def __init__(self):
|
||||
self.events = []
|
||||
|
||||
def operator(self, identifier, **_options):
|
||||
self.events.append(("operator", identifier, True))
|
||||
|
||||
def row(self):
|
||||
return Row(self.events)
|
||||
|
||||
def prop(self, _owner, name):
|
||||
self.events.append(("prop", name, True))
|
||||
|
||||
context = SimpleNamespace(
|
||||
scene=SimpleNamespace(anyimage_settings=SimpleNamespace())
|
||||
)
|
||||
missing = {
|
||||
"environment_ready": False,
|
||||
"missing_models": (),
|
||||
"ready": False,
|
||||
}
|
||||
setup = ("operator", "anyimage.setup_ai_environment", True)
|
||||
with patch.object(self.image_interaction, "ai_status", return_value=missing):
|
||||
for tool, setting_name in (
|
||||
(self.image_box.ImageBoxTool, "image_box_refine_region"),
|
||||
(self.image_lasso.ImageLassoTool, "image_lasso_refine_region"),
|
||||
):
|
||||
layout = Layout()
|
||||
tool.draw_settings(context, layout, None)
|
||||
self.assertEqual(
|
||||
layout.events,
|
||||
[setup, ("prop", setting_name, False)],
|
||||
)
|
||||
|
||||
cutout_tool = importlib.import_module(
|
||||
"anyimage.operators.cutout_tool"
|
||||
).CutoutTool
|
||||
layout = Layout()
|
||||
cutout_tool.draw_settings(context, layout, None)
|
||||
self.assertEqual(
|
||||
layout.events,
|
||||
[
|
||||
setup,
|
||||
("prop", "cutout_refine_region", False),
|
||||
("prop", "cutout_point_spacing", True),
|
||||
("prop", "cutout_generate_normal", False),
|
||||
],
|
||||
)
|
||||
|
||||
layout = Layout()
|
||||
operator = SimpleNamespace(gesture="BOX")
|
||||
self.image_interaction.draw_region_operator_settings(layout, operator)
|
||||
self.assertEqual(
|
||||
layout.events,
|
||||
[
|
||||
setup,
|
||||
("prop", "refine_region", False),
|
||||
("prop", "invert", True),
|
||||
],
|
||||
)
|
||||
|
||||
ready = {"environment_ready": True, "missing_models": (), "ready": True}
|
||||
with patch.object(self.image_interaction, "ai_status", return_value=ready):
|
||||
layout = Layout()
|
||||
cutout_tool.draw_settings(context, layout, None)
|
||||
self.assertNotIn(setup, layout.events)
|
||||
self.assertTrue(all(event[2] for event in layout.events))
|
||||
|
||||
def test_refine_region_is_independent_for_each_area_tool(self):
|
||||
settings = SimpleNamespace(
|
||||
cutout_refine_region=True,
|
||||
@@ -1636,6 +1723,57 @@ class BlenderAddonTest(unittest.TestCase):
|
||||
atol=1e-6,
|
||||
)
|
||||
|
||||
def test_image_perspective_preview_uses_srgb_without_changing_pixels(self):
|
||||
written = []
|
||||
preview = SimpleNamespace(
|
||||
colorspace_settings=SimpleNamespace(name="Non-Color"),
|
||||
alpha_mode=None,
|
||||
pixels=SimpleNamespace(foreach_set=written.extend),
|
||||
update=lambda: None,
|
||||
)
|
||||
previous_data = self.fake_bpy.data
|
||||
self.fake_bpy.data = SimpleNamespace(
|
||||
images=SimpleNamespace(new=lambda *_args, **_options: preview)
|
||||
)
|
||||
pixels = (0.1, 0.2, 0.3, 0.4)
|
||||
source = SimpleNamespace(
|
||||
name="Source.png",
|
||||
colorspace_settings=SimpleNamespace(name="Non-Color"),
|
||||
)
|
||||
try:
|
||||
result = self.image_perspective.create_perspective_preview_image(
|
||||
source, pixels
|
||||
)
|
||||
finally:
|
||||
self.fake_bpy.data = previous_data
|
||||
|
||||
self.assertIs(result, preview)
|
||||
self.assertEqual(preview.colorspace_settings.name, "sRGB")
|
||||
self.assertEqual(preview.alpha_mode, "STRAIGHT")
|
||||
self.assertEqual(written, list(pixels))
|
||||
|
||||
def test_image_perspective_preview_skips_cursor_at_last_clicked_point(self):
|
||||
points = ((10.0, 10.0), (80.0, 10.0), (70.0, 60.0))
|
||||
|
||||
stationary = self.image_perspective.interactive_preview_polygon(
|
||||
points, points[-1]
|
||||
)
|
||||
moving = self.image_perspective.interactive_preview_polygon(
|
||||
points, (20.0, 70.0)
|
||||
)
|
||||
|
||||
self.assertEqual(len(stationary), 3)
|
||||
self.assertEqual(len(moving), 4)
|
||||
|
||||
def test_image_perspective_draws_scene_linear_texture_as_srgb_in_blender_5(self):
|
||||
draw_options = self.image_perspective.preview_texture_draw_options
|
||||
|
||||
self.assertEqual(draw_options((4, 5, 3)), {})
|
||||
self.assertEqual(
|
||||
draw_options((5, 0, 0)),
|
||||
{"is_scene_linear_with_rec709_srgb_target": True},
|
||||
)
|
||||
|
||||
def test_image_perspective_tightly_crops_alpha(self):
|
||||
import numpy as np
|
||||
|
||||
|
||||
Reference in New Issue
Block a user