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
This commit is contained in:
parent
500ec993f5
commit
7168a4fa5c
|
@ -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))
|
||||
|
||||
|
|
|
@ -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)})],
|
||||
),
|
||||
|
||||
]
|
||||
|
||||
|
|
Loading…
Reference in New Issue