diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc b/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc index c4f7a88eb98..38d00eb2226 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.cc @@ -140,7 +140,7 @@ static void apply_new_mask_mesh(const Depsgraph &depsgraph, node_mask.foreach_index(GrainSize(1), [&](const int i, const int pos) { const Span verts = nodes[i].verts(); const Span new_node_mask = new_mask.slice(node_verts[pos]); - if (array_utils::indexed_data_equal(mask, verts, new_mask)) { + if (array_utils::indexed_data_equal(mask, verts, new_node_mask)) { return; } undo::push_node(depsgraph, object, &nodes[i], undo::Type::Mask); diff --git a/tests/files/sculpting/partially_masked_sphere.blend b/tests/files/sculpting/partially_masked_sphere.blend new file mode 100644 index 00000000000..d65110e0b08 --- /dev/null +++ b/tests/files/sculpting/partially_masked_sphere.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:699d53c04cd80fddf1677d0cee252620467f287d232f7223b302736c44f38ad0 +size 7881250 diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 78cd38d7a20..c4d4b18ea3b 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -1263,6 +1263,13 @@ if(TEST_SRC_DIR_EXISTS) -- --testdir "${TEST_SRC_DIR}/sculpting" ) + + add_blender_test( + bl_sculpt_mask_filter + --python ${CMAKE_CURRENT_LIST_DIR}/sculpt_paint/mask_filter_test.py + -- + --testdir "${TEST_SRC_DIR}/sculpting" + ) endif() if(WITH_GPU_MESH_PAINT_TESTS AND TEST_SRC_DIR_EXISTS) diff --git a/tests/python/sculpt_paint/mask_filter_test.py b/tests/python/sculpt_paint/mask_filter_test.py new file mode 100644 index 00000000000..6c83ffa5bed --- /dev/null +++ b/tests/python/sculpt_paint/mask_filter_test.py @@ -0,0 +1,85 @@ +# SPDX-FileCopyrightText: 2025 Blender Authors +# +# SPDX-License-Identifier: GPL-2.0-or-later */ + +__all__ = ( + "main", +) + +import math +import unittest +import sys +import pathlib +import numpy as np + +import bpy + +""" +blender -b --factory-startup --python tests/python/sculpt_paint/mask_filter_test.py -- --testdir tests/files/sculpting/ +""" + +args = None + + +class GrowMaskTest(unittest.TestCase): + def setUp(self): + bpy.ops.wm.open_mainfile(filepath=str(args.testdir / "partially_masked_sphere.blend"), load_ui=False) + bpy.ops.ed.undo_push() + + def test_grow_increases_number_of_masked_vertices(self): + mesh = bpy.context.object.data + mask_attr = mesh.attributes['.sculpt_mask'] + + num_vertices = mesh.attributes.domain_size('POINT') + + old_mask_data = np.zeros(num_vertices, dtype=np.float32) + mask_attr.data.foreach_get('value', old_mask_data) + + bpy.ops.sculpt.mask_filter(filter_type='GROW') + + new_mask_data = np.zeros(num_vertices, dtype=np.float32) + mask_attr.data.foreach_get('value', new_mask_data) + + self.assertGreater(np.count_nonzero(new_mask_data), np.count_nonzero(old_mask_data)) + + +class ShrinkMaskTest(unittest.TestCase): + def setUp(self): + bpy.ops.wm.open_mainfile(filepath=str(args.testdir / "partially_masked_sphere.blend"), load_ui=False) + bpy.ops.ed.undo_push() + + def test_shrink_decreases_number_of_masked_vertices(self): + mesh = bpy.context.object.data + mask_attr = mesh.attributes['.sculpt_mask'] + + num_vertices = mesh.attributes.domain_size('POINT') + + old_mask_data = np.zeros(num_vertices, dtype=np.float32) + mask_attr.data.foreach_get('value', old_mask_data) + + bpy.ops.sculpt.mask_filter(filter_type='SHRINK') + + new_mask_data = np.zeros(num_vertices, dtype=np.float32) + mask_attr.data.foreach_get('value', new_mask_data) + + self.assertLess(np.count_nonzero(new_mask_data), np.count_nonzero(old_mask_data)) + + +def main(): + global args + import argparse + + argv = [sys.argv[0]] + if '--' in sys.argv: + argv += sys.argv[sys.argv.index('--') + 1:] + + parser = argparse.ArgumentParser() + parser.add_argument('--testdir', required=True, type=pathlib.Path) + + args, remaining = parser.parse_known_args(argv) + + unittest.main(argv=remaining) + + +if __name__ == "__main__": + main()