From 7168a4fa5c785c29483947ce60ac797e4b9c1bfc Mon Sep 17 00:00:00 2001 From: Jake Date: Tue, 30 Nov 2021 17:35:57 +1100 Subject: Tests: add edit-mesh operator tests Added operator tests for hide, symmetry_snap, tris_convert_to_quads, uvs_rotate, uvs_rotate, uv_texture_add, uv_texture_remove, vert_connect_concave, vert_connect_nonplanar, vertex_color_add, vertex_color_remove, vertices_smooth_laplacian, wireframe, sculpt_vertex_color_add and sculpt_vertex_color_remove. Ref D11798 Reviewed By: campbellbarton --- tests/python/modules/mesh_test.py | 59 ++++++++++++++---- tests/python/operators.py | 122 +++++++++++++++++++++++++++++++++++++- 2 files changed, 166 insertions(+), 15 deletions(-) (limited to 'tests') diff --git a/tests/python/modules/mesh_test.py b/tests/python/modules/mesh_test.py index 3cee4d88498..99313de92d4 100644 --- a/tests/python/modules/mesh_test.py +++ b/tests/python/modules/mesh_test.py @@ -42,6 +42,7 @@ from abc import ABC, abstractmethod import bpy +import bmesh import functools import inspect import os @@ -102,14 +103,22 @@ class OperatorSpecEditMode: """ Holds one operator and its parameters. """ - - def __init__(self, operator_name: str, operator_parameters: dict, select_mode: str, selection: set): + def __init__( + self, + operator_name: str, + operator_parameters: dict, + select_mode: str, + selection, + *, + select_history: bool = False, + ): """ Constructs an OperatorSpecEditMode. Raises ValueError if selec_mode is invalid. :param operator_name: str - name of mesh operator from bpy.ops.mesh, e.g. "bevel" or "fill" :param operator_parameters: dict - {name : val} dictionary containing operator parameters. :param select_mode: str - mesh selection mode, must be either 'VERT', 'EDGE' or 'FACE' - :param selection: set - set of vertices/edges/faces indices to select, e.g. [0, 9, 10]. + :param selection: sequence - vertices/edges/faces indices to select, e.g. [0, 9, 10]. + :param: select_history: bool - load selection into bmesh selection history. """ self.operator_name = operator_name self.operator_parameters = operator_parameters @@ -117,10 +126,12 @@ class OperatorSpecEditMode: raise ValueError("select_mode must be either {}, {} or {}".format('VERT', 'EDGE', 'FACE')) self.select_mode = select_mode self.selection = selection + self.select_history = select_history def __str__(self): return "Operator: " + self.operator_name + " with parameters: " + str(self.operator_parameters) + \ - " in selection mode: " + self.select_mode + ", selecting " + str(self.selection) + " in selection mode: " + self.select_mode + ", selecting " + str(self.selection) + \ + ("and loading bmesh selection history" if (self.select_history) else "") class OperatorSpecObjectMode: @@ -306,33 +317,51 @@ class MeshTest(ABC): print("\nPASSED {} test successfully.".format(self.test_name)) self._print_result(result) - def do_selection(self, mesh: bpy.types.Mesh, select_mode: str, selection: set): + def do_selection(self, mesh: bpy.types.Mesh, select_mode: str, selection, select_history: bool): """ Do selection on a mesh. :param mesh: bpy.types.Mesh - input mesh :param: select_mode: str - selection mode. Must be 'VERT', 'EDGE' or 'FACE' - :param: selection: set - indices of selection. + :param: selection: sequence - indices of selection. + :param: select_history: bool - load selection into bmesh selection history Example: select_mode='VERT' and selection={1,2,3} selects veritces 1, 2 and 3 of input mesh """ + if select_history and isinstance(selection, set): + raise Exception("'selection' must be an ordered sequence, not a 'set' type when 'select_history=True'") + # Deselect all objects. bpy.ops.object.mode_set(mode='EDIT') bpy.ops.mesh.select_all(action='DESELECT') - bpy.ops.object.mode_set(mode='OBJECT') + + bm = bmesh.from_edit_mesh(mesh) + + #bpy.ops.object.mode_set(mode='OBJECT') bpy.context.tool_settings.mesh_select_mode = (select_mode == 'VERT', select_mode == 'EDGE', select_mode == 'FACE') - items = (mesh.vertices if select_mode == 'VERT' - else mesh.edges if select_mode == 'EDGE' - else mesh.polygons if select_mode == 'FACE' - else None) + items = ( + bm.verts if select_mode == 'VERT' else + bm.edges if select_mode == 'EDGE' else + bm.faces if select_mode == 'FACE' else None + ) + + items.ensure_lookup_table() + if items is None: raise ValueError("Invalid selection mode") for index in selection: items[index].select = True + if select_history: + for index in selection: + bm.select_history.add(items[index]) + bm.select_history.validate() + + bpy.ops.object.mode_set(mode='OBJECT') + def update_failed_test(self): """ Updates expected object. @@ -639,7 +668,11 @@ class SpecMeshTest(MeshTest): :param operator: OperatorSpecEditMode - OperatorSpecEditMode object with parameters. """ self.do_selection( - test_object.data, operator.select_mode, operator.selection) + test_object.data, + operator.select_mode, + operator.selection, + select_history=operator.select_history, + ) # Apply operator in edit mode. bpy.ops.object.mode_set(mode='EDIT') @@ -654,7 +687,7 @@ class SpecMeshTest(MeshTest): raise TypeError("Incorrect operator parameters {!r} raised {!r}".format(operator.operator_parameters, ex)) if retval != {'FINISHED'}: - raise RuntimeError("Unexpected operator return value: {}".format(retval)) + raise RuntimeError("Unexpected operator return value: {}".format(operator.operator_name)) if self.verbose: print("Applied {}".format(operator)) diff --git a/tests/python/operators.py b/tests/python/operators.py index 9e5ac0054e8..e2a5e78cff7 100644 --- a/tests/python/operators.py +++ b/tests/python/operators.py @@ -204,6 +204,14 @@ def main(): SpecMeshTest("CubeShadeFlat", "testCubeShadeFlat", "expectedCubeShadeFlat", [OperatorSpecEditMode("faces_shade_flat", {}, "FACE", {i for i in range(6)})]), + # hide + SpecMeshTest("HideFace", "testCubeHideFace", "expectedCubeHideFace", + [OperatorSpecEditMode("hide", {}, "FACE", {3})]), + SpecMeshTest("HideEdge", "testCubeHideEdge", "expectedCubeHideEdge", + [OperatorSpecEditMode("hide", {}, "EDGE", {1})]), + SpecMeshTest("HideVertex", "testCubeHideVertex", "expectedCubeHideVertex", + [OperatorSpecEditMode("hide", {}, "VERT", {0})]), + # inset faces SpecMeshTest("CubeInset", "testCubeInset", "expectedCubeInset", [OperatorSpecEditMode("inset", {"thickness": 0.2}, "VERT", @@ -312,11 +320,60 @@ def main(): SpecMeshTest("CircleSelect2nd", "testCircleSelect2nd", "expectedCircleSelect2nd", [OperatorSpecEditMode("select_nth", {}, "VERT", {i for i in range(32)})]), + # Subdivide edgering - Not currently functional, operator returns inconsistently + #SpecMeshTest("SubdivideEdgeringSurface", "testCylinderSubdivideEdgering", "expectedCylinderSubdivideEdgeringSurface", + # [OperatorSpecEditMode("subdivide_edgering", {"number_cuts": 5, "interpolation": 'SURFACE', "profile_shape_factor": 0.1}, "EDGE", {0, (i for i in range(96) if (i % 3))})]), + #SpecMeshTest("SubdivideEdgeringPath", "testCylinderSubdivideEdgering", "expectedCylinderSubdivideEdgeringPath", + # [OperatorSpecEditMode("subdivide_edgering", {"number_cuts": 5, "interpolation": 'PATH', "profile_shape_factor": 0.1}, "EDGE", {0, (i for i in range(96) if (i % 3))})]), + #SpecMeshTest("SubdivideEdgeringLinear", "testCylinderSubdivideEdgering", "expectedCylinderSubdivideEdgeringLinear", + # [OperatorSpecEditMode("subdivide_edgering", {"number_cuts": 5, "interpolation": 'LINEAR', "profile_shape_factor": 0.1}, "EDGE", {0, (i for i in range(96) if (i % 3))})]), + + # Symmetry Snap + SpecMeshTest("SymmetrySnap", "testPlaneSymmetrySnap", "expectedPlaneSymmetrySnap", + [OperatorSpecEditMode("symmetry_snap", {"direction": 'POSITIVE_X', "threshold": 1, "factor": 0.75, + "use_center": False}, "VERT", {i for i in range(5)})]), + SpecMeshTest("SymmetrySnapCenter", "testPlaneSymmetrySnap", "expectedPlaneSymmetrySnapCenter", + [OperatorSpecEditMode("symmetry_snap", {"direction": 'NEGATIVE_X', "threshold": 1, "factor": 0.75, + "use_center": True}, "VERT", {i for i in range(5)})]), + + # Tris to Quads + SpecMeshTest("TrisToQuads", "testPlanesTrisToQuad", "expectedPlanesTrisToQuad", + [OperatorSpecEditMode("tris_convert_to_quads", {"face_threshold":0.174533, "shape_threshold":0.174533, + "uvs":True, "vcols":True, "seam":True, "sharp":True, "materials":True}, "VERT", {i for i in range(32)})]), + # unsubdivide # normal case SpecMeshTest("CubeFaceUnsubdivide", "testCubeUnsubdivide", "expectedCubeUnsubdivide", [OperatorSpecEditMode("unsubdivide", {}, "FACE", {i for i in range(6)})]), + # UV Manipulation + SpecMeshTest("UVRotate", "testCubeUV", "expectedCubeUVRotate", + [OperatorSpecEditMode("uvs_rotate", {}, "FACE", {2})]), + SpecMeshTest("UVRotateCCW", "testCubeUV", "expectedCubeUVRotateCCW", + [OperatorSpecEditMode("uvs_rotate", {"use_ccw": True}, "FACE", {2})]), + SpecMeshTest("UVReverse", "testCubeUV", "expectedCubeUVReverse", + [OperatorSpecEditMode("uvs_reverse", {}, "FACE", {2})]), + SpecMeshTest("UVAdd", "testCubeUV", "expectedCubeUVAdd", + [OperatorSpecEditMode("uv_texture_add", {}, "FACE", {})]), + SpecMeshTest("UVRemove", "testCubeUV", "expectedCubeUVRemove", + [OperatorSpecEditMode("uv_texture_remove", {}, "FACE", {})]), + + + # Vert Connect Concave + SpecMeshTest("VertexConnectConcave", "testPlaneVertConnectConcave", "expectedPlaneVertConnectConcave", + [OperatorSpecEditMode("vert_connect_concave", {}, "FACE", {0})]), + SpecMeshTest("VertexConnectConcaveConvexPentagon", "testPentagonVertConnectConcave", "expectedPentagonVertConnectConcave", + [OperatorSpecEditMode("vert_connect_concave", {}, "FACE", {0})]), + SpecMeshTest("VertexConnectConcaveQuad", "testPlaneVertConnectConcaveQuad", "expectedPlaneVertConnectConcaveQuad", + [OperatorSpecEditMode("vert_connect_concave", {}, "FACE", {0})]), + + # Vert Connect Nonplanar + SpecMeshTest("VertexConnectNonplanar", "testPlaneVertConnectNonplanar", "expectedPlaneVertConnectNonplanar", + [OperatorSpecEditMode("vert_connect_nonplanar", {"angle_limit": 0.17453292}, "VERT", {i for i in range(9)})]), + SpecMeshTest("VertexConnectNonplanarNgon", "testPlaneVertConnectNonplanarNgon", "expectedPlaneVertConnectNonplanarNgon", + [OperatorSpecEditMode("vert_connect_nonplanar", {"angle_limit": 0.218166}, "VERT", {i for i in range(6)})]), + + # T87259 - test cases SpecMeshTest("CubeEdgeUnsubdivide", "testCubeEdgeUnsubdivide", "expectedCubeEdgeUnsubdivide", [OperatorSpecEditMode("unsubdivide", {}, "EDGE", {i for i in range(6)})]), @@ -325,8 +382,69 @@ def main(): # vert connect path # Tip: It works only if there is an already existing face or more than 2 vertices. - SpecMeshTest("CubeVertConnectPath", "testCubeVertConnectPath", "expectedCubeVertConnectPath", - [OperatorSpecEditMode("vert_connect_path", {}, "VERT", {0, 5})]), + SpecMeshTest( + "PlaneVertConnectPath", "testPlaneVertConnectPath", "expectedPlaneVertConnectPath", + [OperatorSpecEditMode( + "vert_connect_path", {}, "VERT", (0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14), + select_history=True, + )], + ), + + # Vertex Colors + SpecMeshTest( + "VertexColorAdd", "testCubeColorAdd", "expectedCubeColorAdd", + [OperatorSpecEditMode("vertex_color_add", {}, "VERT", {})], + ), + SpecMeshTest( + "VertexColorRemove", "testCubeColorRemove", "expectedCubeColorRemove", + [OperatorSpecEditMode("vertex_color_remove", {}, "VERT", {})], + ), + SpecMeshTest( + "VertexColorSculptAdd", "testCubeSculptAdd", "expectedCubeSculptAdd", + [OperatorSpecEditMode("sculpt_vertex_color_add", {}, "VERT", {})], + ), + SpecMeshTest( + "VertexColorSculptRemove", "testCubeSculptRemove", "expectedCubeSculptRemove", + [OperatorSpecEditMode("sculpt_vertex_color_remove", {}, "VERT", {})], + ), + + # Laplacian Smooth + SpecMeshTest( + "LaplacianSmoothDefault", "testSphereLaplacianSmoothDefault", "expectedSphereLaplacianSmoothDefault", + [OperatorSpecEditMode("vertices_smooth_laplacian", {"preserve_volume": False}, "VERT", {i for i in range(482)})], + ), + SpecMeshTest( + "LaplacianSmoothHighValues", "testSphereLaplacianSmoothHigh", "expectedSphereLaplacianSmoothHigh", + [OperatorSpecEditMode("vertices_smooth_laplacian", {"preserve_volume": False, "repeat": 100, "lambda_factor": 10.0}, "VERT", {i for i in range(482)})], + ), + SpecMeshTest( + "LaplacianSmoothBorder", "testCubeLaplacianSmoothBorder", "expectedCubeLaplacianSmoothBorder", + [OperatorSpecEditMode("vertices_smooth_laplacian", {"preserve_volume": False, "lambda_border": 1.0}, "VERT", {i for i in range(25)})], + ), + SpecMeshTest( + "LaplacianSmoothHighBorder", "testCubeLaplacianSmoothHighBorder", "expectedCubeLaplacianSmoothHighBorder", + [OperatorSpecEditMode("vertices_smooth_laplacian", {"preserve_volume": False, "lambda_border": 100.0}, "VERT", {i for i in range(25)})], + ), + SpecMeshTest( + "LaplacianSmoothPreserveVolume", "testSphereLaplacianSmoothPreserveVol", "expectedSphereLaplacianSmoothPreserveVol", + [OperatorSpecEditMode("vertices_smooth_laplacian", {"preserve_volume": True}, "VERT", {i for i in range(482)})], + ), + + + # wireframe + SpecMeshTest( + "WireFrameDefault", "testCubeWireframeDefault", "expectedCubeWireframeDefault", + [OperatorSpecEditMode("wireframe", {}, "FACE", {i for i in range(6)})], + ), + SpecMeshTest( + "WireFrameAlt", "testCubeWireframeAlt", "expectedCubeWireframeAlt", + [OperatorSpecEditMode( + "wireframe", { + "use_boundary": False, "use_even_offset": False, + "use_relative_offset": True, "use_replace": False, "thickness": 0.3, "offset": 0.3, + "use_crease": True, "crease_weight": 0.01, + }, "FACE", {i for i in range(6)})], + ), ] -- cgit v1.2.3