This commit is contained in:
327
tests/anyimage/operators/test_frame_tool.py
Normal file
327
tests/anyimage/operators/test_frame_tool.py
Normal file
@@ -0,0 +1,327 @@
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import Mock, patch
|
||||
from tests.support.blender import BlenderTestCase
|
||||
|
||||
|
||||
class FrameToolTest(BlenderTestCase):
|
||||
def test_frame_builds_a_canonical_rectangular_screen_path(self):
|
||||
self.assertEqual(
|
||||
self.image_interaction.rectangle_screen_path((2, 3), (8, 9)),
|
||||
((2.0, 9.0), (8.0, 9.0), (8.0, 3.0), (2.0, 3.0)),
|
||||
)
|
||||
self.assertEqual(
|
||||
self.image_interaction.rectangle_screen_path((2, 3), (3, 9)),
|
||||
(),
|
||||
)
|
||||
|
||||
|
||||
def test_frame_reports_missing_active_overlap_as_warning(self):
|
||||
active = SimpleNamespace(name="Active")
|
||||
context = SimpleNamespace(
|
||||
active_object=active,
|
||||
region=object(),
|
||||
region_data=SimpleNamespace(view_matrix="view-matrix"),
|
||||
)
|
||||
descriptor = {
|
||||
"active": True,
|
||||
}
|
||||
|
||||
with (
|
||||
patch.object(self.image_frame, "_frame_source", return_value=descriptor),
|
||||
patch.object(self.image_frame, "frame_source_overlap", return_value=()),
|
||||
self.assertRaises(self.image_selection.ImageEditWarning),
|
||||
):
|
||||
self.image_frame.apply_frame(
|
||||
context,
|
||||
(active,),
|
||||
((2, 3), (3, 3), (3, 2), (2, 2)),
|
||||
)
|
||||
|
||||
|
||||
def test_frame_uses_selected_still_images_and_ignores_other_objects(self):
|
||||
active = SimpleNamespace(
|
||||
name="Active",
|
||||
type="EMPTY",
|
||||
empty_display_type="IMAGE",
|
||||
data=SimpleNamespace(source="FILE"),
|
||||
)
|
||||
other = SimpleNamespace(
|
||||
name="Other",
|
||||
type="EMPTY",
|
||||
empty_display_type="IMAGE",
|
||||
data=SimpleNamespace(source="FILE"),
|
||||
)
|
||||
mesh = SimpleNamespace(name="Mesh", type="MESH")
|
||||
context = SimpleNamespace(
|
||||
active_object=active,
|
||||
selected_objects=(mesh, active, other),
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
self.image_frame.selected_frame_sources(context),
|
||||
(other, active),
|
||||
)
|
||||
|
||||
|
||||
def test_frame_rejects_active_image_that_is_not_selected(self):
|
||||
active = SimpleNamespace(
|
||||
name="Active",
|
||||
type="EMPTY",
|
||||
empty_display_type="IMAGE",
|
||||
data=SimpleNamespace(source="FILE"),
|
||||
)
|
||||
other = SimpleNamespace(
|
||||
name="Other",
|
||||
type="EMPTY",
|
||||
empty_display_type="IMAGE",
|
||||
data=SimpleNamespace(source="FILE"),
|
||||
)
|
||||
context = SimpleNamespace(
|
||||
active_object=active,
|
||||
selected_objects=(other,),
|
||||
)
|
||||
|
||||
with self.assertRaisesRegex(
|
||||
self.image_frame.ImageEditWarning,
|
||||
"Select an active Image Empty",
|
||||
):
|
||||
self.image_frame.selected_frame_sources(context)
|
||||
|
||||
|
||||
def test_frame_warns_when_active_is_not_an_image(self):
|
||||
image = SimpleNamespace(
|
||||
name="Image",
|
||||
type="EMPTY",
|
||||
empty_display_type="IMAGE",
|
||||
data=SimpleNamespace(source="FILE"),
|
||||
)
|
||||
mesh = SimpleNamespace(name="Mesh", type="MESH")
|
||||
context = SimpleNamespace(
|
||||
active_object=mesh,
|
||||
selected_objects=(image, mesh),
|
||||
area=SimpleNamespace(type="VIEW_3D"),
|
||||
)
|
||||
operator = self.image_frame.FrameImages()
|
||||
operator.report = Mock()
|
||||
|
||||
self.assertTrue(operator.poll(context))
|
||||
self.assertEqual(operator.invoke(context, object()), {"CANCELLED"})
|
||||
operator.report.assert_called_once_with(
|
||||
{"WARNING"},
|
||||
"Select an active Image Empty to use an Image tool",
|
||||
)
|
||||
|
||||
|
||||
def test_frame_modal_can_start_outside_source_projection(self):
|
||||
active = SimpleNamespace(name="Active")
|
||||
other = SimpleNamespace(name="Other")
|
||||
redraw = Mock()
|
||||
status = Mock()
|
||||
modal_handler_add = Mock()
|
||||
context = SimpleNamespace(
|
||||
active_object=active,
|
||||
area=SimpleNamespace(as_pointer=lambda: 1, tag_redraw=redraw),
|
||||
region=SimpleNamespace(as_pointer=lambda: 2),
|
||||
workspace=SimpleNamespace(status_text_set=status),
|
||||
window_manager=SimpleNamespace(modal_handler_add=modal_handler_add),
|
||||
)
|
||||
event = SimpleNamespace(mouse_region_x=-20, mouse_region_y=300)
|
||||
space = SimpleNamespace(
|
||||
draw_handler_add=Mock(return_value="handle"),
|
||||
draw_handler_remove=Mock(),
|
||||
)
|
||||
operator = self.image_frame.FrameImages()
|
||||
|
||||
with (
|
||||
patch.object(
|
||||
self.image_frame,
|
||||
"selected_frame_sources",
|
||||
return_value=(other, active),
|
||||
),
|
||||
patch.object(
|
||||
self.image_frame.bpy.types,
|
||||
"SpaceView3D",
|
||||
space,
|
||||
create=True,
|
||||
),
|
||||
):
|
||||
result = operator.invoke(context, event)
|
||||
operator.cancel(context)
|
||||
|
||||
self.assertEqual(result, {"RUNNING_MODAL"})
|
||||
self.assertEqual(operator._active_name, "Active")
|
||||
self.assertEqual(operator._source_names, ("Other", "Active"))
|
||||
self.assertEqual(operator._start, (-20.0, 300.0))
|
||||
modal_handler_add.assert_called_once_with(operator)
|
||||
space.draw_handler_remove.assert_called_once_with("handle", "WINDOW")
|
||||
|
||||
|
||||
def test_frame_rejects_an_animated_selected_image(self):
|
||||
active = SimpleNamespace(
|
||||
name="Active",
|
||||
type="EMPTY",
|
||||
empty_display_type="IMAGE",
|
||||
data=SimpleNamespace(source="FILE"),
|
||||
)
|
||||
video = SimpleNamespace(
|
||||
name="Video",
|
||||
type="EMPTY",
|
||||
empty_display_type="IMAGE",
|
||||
data=SimpleNamespace(source="MOVIE", frame_duration=2),
|
||||
)
|
||||
context = SimpleNamespace(
|
||||
active_object=active,
|
||||
selected_objects=(active, video),
|
||||
)
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "still images"):
|
||||
self.image_frame.selected_frame_sources(context)
|
||||
|
||||
|
||||
def test_frame_keeps_active_object_and_removes_other_image_empties(self):
|
||||
import numpy as np
|
||||
|
||||
class MatrixStub:
|
||||
translation = SimpleNamespace(copy=lambda: "active-origin")
|
||||
|
||||
def copy(self):
|
||||
return "old-matrix"
|
||||
|
||||
active_image = Mock(users=1, use_fake_user=False)
|
||||
other_image = Mock(users=1, use_fake_user=False)
|
||||
active = SimpleNamespace(
|
||||
name="Active",
|
||||
data=active_image,
|
||||
matrix_world=MatrixStub(),
|
||||
empty_display_size=1.0,
|
||||
empty_image_offset=(-0.5, -0.5),
|
||||
)
|
||||
other = SimpleNamespace(name="Other", data=other_image)
|
||||
context = SimpleNamespace(
|
||||
active_object=active,
|
||||
region=SimpleNamespace(width=100, height=100),
|
||||
region_data=SimpleNamespace(view_matrix="view-matrix"),
|
||||
)
|
||||
frame = ((0.0, 2.0), (2.0, 2.0), (2.0, 0.0), (0.0, 0.0))
|
||||
descriptors = iter(
|
||||
(
|
||||
{
|
||||
"active": False,
|
||||
"image_size": (2, 2),
|
||||
},
|
||||
{
|
||||
"active": True,
|
||||
"image_size": (2, 2),
|
||||
},
|
||||
)
|
||||
)
|
||||
result_image = Mock(users=1)
|
||||
remove_object = Mock()
|
||||
|
||||
def replace(source, result, *, removed_objects):
|
||||
self.assertEqual(removed_objects, (other,))
|
||||
source.data = result
|
||||
|
||||
with (
|
||||
patch.object(self.image_frame, "_frame_source", side_effect=descriptors),
|
||||
patch.object(
|
||||
self.image_frame,
|
||||
"frame_source_overlap",
|
||||
return_value=frame,
|
||||
),
|
||||
patch.object(self.image_frame, "frame_output_size", return_value=(2, 2)),
|
||||
patch.object(
|
||||
self.image_frame,
|
||||
"composite_frame_pixels",
|
||||
return_value=np.zeros(16, dtype=np.float32),
|
||||
),
|
||||
patch.object(
|
||||
self.image_frame,
|
||||
"frame_result_transform",
|
||||
return_value=("new-matrix", 2.0),
|
||||
),
|
||||
patch.object(
|
||||
self.image_frame,
|
||||
"frame_depth_location",
|
||||
return_value="active-origin",
|
||||
),
|
||||
patch.object(
|
||||
self.image_frame,
|
||||
"create_image_edit_result",
|
||||
return_value=result_image,
|
||||
),
|
||||
patch.object(self.image_frame, "replace_empty_image", side_effect=replace),
|
||||
patch.object(
|
||||
self.image_frame.bpy.data,
|
||||
"objects",
|
||||
SimpleNamespace(remove=remove_object),
|
||||
),
|
||||
patch.object(
|
||||
self.image_frame.bpy.data,
|
||||
"images",
|
||||
SimpleNamespace(remove=Mock()),
|
||||
create=True,
|
||||
),
|
||||
):
|
||||
result = self.image_frame.apply_frame(
|
||||
context,
|
||||
(other, active),
|
||||
frame,
|
||||
)
|
||||
|
||||
self.assertIs(result, active)
|
||||
self.assertIs(active.data, result_image)
|
||||
self.assertEqual(active.matrix_world, "new-matrix")
|
||||
self.assertEqual(active.empty_display_size, 2.0)
|
||||
self.assertEqual(active.empty_image_offset, (-0.5, -0.5))
|
||||
remove_object.assert_called_once_with(other, do_unlink=True)
|
||||
|
||||
|
||||
def test_frame_invalid_result_depth_does_not_create_or_replace_image(self):
|
||||
import numpy as np
|
||||
|
||||
active_image = Mock(users=1, use_fake_user=False)
|
||||
active = SimpleNamespace(
|
||||
name="Active",
|
||||
data=active_image,
|
||||
matrix_world=SimpleNamespace(
|
||||
translation=SimpleNamespace(copy=lambda: "active-origin")
|
||||
),
|
||||
)
|
||||
context = SimpleNamespace(
|
||||
active_object=active,
|
||||
region=SimpleNamespace(width=100, height=100),
|
||||
region_data=SimpleNamespace(view_matrix="view-matrix"),
|
||||
)
|
||||
frame = ((0.0, 2.0), (2.0, 2.0), (2.0, 0.0), (0.0, 0.0))
|
||||
descriptor = {"active": True, "image_size": (2, 2)}
|
||||
create_result = Mock()
|
||||
replace_result = Mock()
|
||||
|
||||
with (
|
||||
patch.object(self.image_frame, "_frame_source", return_value=descriptor),
|
||||
patch.object(self.image_frame, "frame_source_overlap", return_value=frame),
|
||||
patch.object(self.image_frame, "frame_output_size", return_value=(2, 2)),
|
||||
patch.object(
|
||||
self.image_frame,
|
||||
"composite_frame_pixels",
|
||||
return_value=np.zeros(16, dtype=np.float32),
|
||||
),
|
||||
patch.object(
|
||||
self.image_frame,
|
||||
"frame_depth_location",
|
||||
side_effect=ValueError("The Frame result depth is invalid"),
|
||||
),
|
||||
patch.object(
|
||||
self.image_frame,
|
||||
"create_image_edit_result",
|
||||
create_result,
|
||||
),
|
||||
patch.object(self.image_frame, "replace_empty_image", replace_result),
|
||||
self.assertRaisesRegex(ValueError, "result depth"),
|
||||
):
|
||||
self.image_frame.apply_frame(context, (active,), frame)
|
||||
|
||||
create_result.assert_not_called()
|
||||
replace_result.assert_not_called()
|
||||
self.assertIs(active.data, active_image)
|
||||
Reference in New Issue
Block a user