Не подтверждена Коммит c4274f83 создал по автору Ilya V. Portnov's avatar Ilya V. Portnov Зафиксировано автором GitHub
Просмотр файлов

Merge pull request #2542 from nortikin/b28_calc_mask

Port "calc_mask" node to 2.80 branch
владельцы a2aeea3d 8173e7f5
......@@ -367,6 +367,42 @@ def describe_data_shape(data):
nesting, result = helper(data)
return "Level {}: {}".format(nesting, result)
def calc_mask(subset_data, set_data, level=0, negate=False, ignore_order=True):
"""
Calculate mask: for each item in set_data, return True if it is present in subset_data.
The function can work at any specified level.
subset_data: subset, for example [1]
set_data: set, for example [1, 2, 3]
level: 0 to check immediate members of set and subset; 1 to work with lists of lists and so on.
negate: if True, then result will be negated (True if item of set is not present in subset).
ignore_order: when comparing lists, ignore items order.
Raises an exception if nesting level of input sets is less than specified level parameter.
calc_mask([1], [1,2,3]) == [True, False, False])
calc_mask([1], [1,2,3], negate=True) == [False, True, True]
"""
if level == 0:
if not isinstance(subset_data, (tuple, list)):
raise Exception("Specified level is too high for given Subset")
if not isinstance(set_data, (tuple, list)):
raise Exception("Specified level is too high for given Set")
if ignore_order and get_data_nesting_level(subset_data) > 1:
if negate:
return [set(item) not in map(set, subset_data) for item in set_data]
else:
return [set(item) in map(set, subset_data) for item in set_data]
else:
if negate:
return [item not in subset_data for item in set_data]
else:
return [item in subset_data for item in set_data]
else:
sub_objects = match_long_repeat([subset_data, set_data])
return [calc_mask(subset_item, set_item, level - 1, negate, ignore_order) for subset_item, set_item in zip(*sub_objects)]
#####################################################
################### matrix magic ####################
#####################################################
......
Calculate Mask
==============
Functionality
-------------
This node calculates masks from two input lists (Set and Subset). For each item
in the Set, it returns True if the item is present in Subset, otherwise it
returns False.
There are nodes, which output, for example, "All faces" and "Processed faces"
or smth like that. To do something with that output, it would be usually more
effective to deal with "All faces" and "Processed faces mask" instead.
The node can work on different levels of data trees. For example, given Subset
= `[[1, 2], [3,4]]` and Set = `[[1, 2], [3, 4], [5, 6]]`:
* with level = 0 it will output `[True, True, False]`
* with level = 1 it will output `[[True, True], [True, True], [False, False]]`
Given Subset = `[[1], [5,6]]` and Set = `[[1, 2, 3], [7, 8, 9]]`:
* with level = 0 it will output `[False, False]` (because, for example, there
is no `[1, 2, 3]` in the `[[1], [5,6]]`)
* with level = 1, it will output `[[True, False, False], [False, False, False]]`
Inputs
------
This node has the following inputs:
* **Subset**. List of "good" data items to be checked against.
* **Set**. The whole set of data items.
Parameters
----------
This node has the following parameters:
* **Negate**. If checked, then the resulting mask will be negated. I.e., the
node will output True if item of Set is *not* present in Subset. Unchecked by
default.
* **Ignore order**. If checked, then, while comparing lists, the node will not
take into account the order of items in these lists. For example, is `[1, 2]`
the same as `[2, 1]`? No, if **Ignore order** is not checked.
Outputs
-------
This node has only one output: **Mask**. Number of items in this outuput is
equal to number of items in the **Set** input.
Example of usage
----------------
This node can, for example, be used to apply **Inset Special** node iteratively:
.. image:: https://user-images.githubusercontent.com/284644/58757902-82715a00-852d-11e9-9288-369607f5229d.png
......@@ -10,3 +10,5 @@ List Masks
mask_converter
mask_to_index
index_to_mask
calc_mask
......@@ -138,6 +138,7 @@
SvMaskConvertNode
SvMaskToIndexNode
SvIndexToMaskNode
SvCalcMaskNode
## List Mutators
SvListModifierNode
......@@ -358,4 +359,4 @@
SvContourNode
SvPlanarEdgenetToPolygons
SvPulgaPhysicsNode
SvTopologySimple
\ Нет новой строки в конце файла
SvTopologySimple
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
import bpy
from bpy.props import BoolProperty, EnumProperty, IntProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.data_structure import updateNode, match_long_repeat, fullList, calc_mask
class SvCalcMaskNode(bpy.types.Node, SverchCustomTreeNode):
"""
Triggers: Calculate Mask
Tooltip: Calculate mask from two sets of objects
"""
bl_idname = 'SvCalcMaskNode'
bl_label = 'Calculate Mask'
bl_icon = 'OUTLINER_OB_EMPTY'
level : IntProperty(name = 'Level',
description = "List level to operate on",
min = 0, default = 0, update=updateNode)
negate : BoolProperty(name = 'Negate',
description = 'Negate mask', update=updateNode)
ignore_order : BoolProperty(name = 'Ignore order',
description = "Ignore items order while comparing lists",
default = True, update=updateNode)
def draw_buttons(self, context, layout):
layout.prop(self, 'level')
layout.prop(self, 'negate')
layout.prop(self, 'ignore_order')
def sv_init(self, context):
self.inputs.new('SvStringsSocket', "Subset")
self.inputs.new('SvStringsSocket', "Set")
self.outputs.new('SvStringsSocket', 'Mask')
def process(self):
if not any(output.is_linked for output in self.outputs):
return
subset_s = self.inputs['Subset'].sv_get(default=[[]])
set_s = self.inputs['Set'].sv_get(default=[[]])
out_masks = []
objects = match_long_repeat([subset_s, set_s])
for subset, set in zip(*objects):
mask = calc_mask(subset, set, level=self.level, negate=self.negate, ignore_order=self.ignore_order)
out_masks.append(mask)
self.outputs['Mask'].sv_set(out_masks)
def register():
bpy.utils.register_class(SvCalcMaskNode)
def unregister():
bpy.utils.unregister_class(SvCalcMaskNode)
......@@ -68,3 +68,53 @@ class DataStructureTests(SverchokTestCase):
self.subtest_assert_equals(describe_data_shape([1]), 'Level 1: list [1] of int')
self.subtest_assert_equals(describe_data_shape([[(1,2,3)]]), 'Level 3: list [1] of list [1] of tuple [3] of int')
class CalcMaskTests(SverchokTestCase):
def test_calc_mask_1(self):
subset = [1]
set = [1, 2, 3]
mask = calc_mask(subset, set, level=0)
expected = [True, False, False]
self.assertEquals(mask, expected)
def test_calc_mask_2(self):
subset = [1]
set = [1, 2, 3]
mask = calc_mask(subset, set, negate=True)
expected = [False, True, True]
self.assertEquals(mask, expected)
def test_calc_mask_3(self):
subset = [[1, 2], [3, 4]]
set = [[1, 2], [3, 4], [5, 6]]
mask = calc_mask(subset, set, level=0)
expected = [True, True, False]
self.assertEquals(mask, expected)
def test_calc_mask_4(self):
subset = [[1, 2], [3, 4]]
set = [[1, 2], [3, 4], [5, 6]]
mask = calc_mask(subset, set, level=1)
expected = [[True, True], [True, True], [False, False]]
self.assertEquals(mask, expected)
def test_calc_mask_5(self):
subset = [[1], [5,6]]
set = [[1, 2, 3], [7, 8, 9]]
mask = calc_mask(subset, set, level=0)
expected = [False, False]
self.assertEquals(mask, expected)
def test_calc_mask_6(self):
subset = [[1], [5,6]]
set = [[1, 2, 3], [7, 8, 9]]
mask = calc_mask(subset, set, level=1)
expected = [[True, False, False], [False, False, False]]
self.assertEquals(mask, expected)
def test_calc_mask_7(self):
subset = [[1, 2], [3, 4]]
set = [[2, 1], [5, 6]]
mask = calc_mask(subset, set, ignore_order=True)
expected = [True, False]
self.assertEquals(mask, expected)
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать