Открыть боковую панель
nikitronn
sverchok
Коммиты
415efa52
Не подтверждена
Коммит
415efa52
создал
Дек 05, 2020
по автору
Ilya V. Portnov
Зафиксировано автором
GitHub
Дек 05, 2020
Просмотр файлов
Merge pull request #3753 from nortikin/list_levels_node
"List levels" node
владельцы
df703070
43cc6256
Изменения
7
Скрыть пробелы
Построчно
Рядом
core/socket_data.py
Просмотр файла @
415efa52
...
...
@@ -105,6 +105,8 @@ def SvGetSocket(socket, deepcopy=True):
global
socket_data_cache
if
socket
.
is_linked
:
other
=
socket
.
other
if
other
is
None
:
raise
SvNoDataError
(
socket
)
s_id
=
other
.
socket_id
s_ng
=
other
.
id_data
.
tree_id
if
s_ng
not
in
socket_data_cache
:
...
...
data_structure.py
Просмотр файла @
415efa52
...
...
@@ -610,6 +610,39 @@ def unwrap_data(data, unwrap_level=1, socket=None):
data
=
unwrap
(
data
,
level
)
return
data
class
SvListLevelAdjustment
(
object
):
def
__init__
(
self
,
flatten
=
False
,
wrap
=
False
):
self
.
flatten
=
flatten
self
.
wrap
=
wrap
def
__repr__
(
self
):
return
f
"<Flatten=
{
self
.
flatten
}
, Wrap=
{
self
.
wrap
}
>"
def
list_levels_adjust
(
data
,
instructions
,
data_types
=
SIMPLE_DATA_TYPES
):
data_level
=
get_data_nesting_level
(
data
,
data_types
+
(
ndarray
,))
if
len
(
instructions
)
<
data_level
+
1
:
raise
Exception
(
f
"Number of instructions (
{
len
(
instructions
)
}
) is less than data nesting level
{
data_level
}
+ 1"
)
def
process
(
data
,
instruction
,
level
):
result
=
data
if
level
+
1
<
data_level
and
instruction
.
flatten
:
result
=
sum
(
result
,
[])
if
instruction
.
wrap
:
result
=
[
result
]
#print(f"II: {level}/{data_level}, {instruction}, {data} => {result}")
return
result
def
helper
(
data
,
instructions
,
level
):
if
level
==
data_level
:
items
=
process
(
data
,
instructions
[
0
],
level
)
else
:
sub_items
=
[
helper
(
item
,
instructions
[
1
:],
level
+
1
)
for
item
in
data
]
items
=
process
(
sub_items
,
instructions
[
0
],
level
)
#print(f"?? {level}/{data_level}, {data} => {sub_items} => {items}")
return
items
return
helper
(
data
,
instructions
,
0
)
def
map_at_level
(
function
,
data
,
item_level
=
0
,
data_types
=
SIMPLE_DATA_TYPES
):
"""
Given a nested list of object, apply `function` to each sub-list of items.
...
...
@@ -638,37 +671,52 @@ def split_by_count(iterable, n, fillvalue=None):
args
=
[
iter
(
iterable
)]
*
n
return
list
(
map
(
list
,
zip_longest
(
*
args
,
fillvalue
=
fillvalue
)))
def
describe_data_shape
(
data
):
def
describe_data_shape
_by_level
(
data
,
include_numpy_nesting
=
True
):
"""
Describe shape of data in human-readable form.
Returns string.
Can be used for debugging or for displaying information to user.
Note: this method inspects only first element of each list/tuple,
expecting they are all homogenous (that is usually true in Sverchok).
describe_data_shape(None) == 'Level 0: NoneType'
describe_data_shape(1) == 'Level 0: int'
describe_data_shape([]) == 'Level 1: list [0]'
describe_data_shape([1]) == 'Level 1: list [1] of int'
describe_data_shape([[(1,2,3)]]) == 'Level 3: list [1] of list [1] of tuple [3] of int'
Returns tuple:
* data nesting level
* list of descriptions of data shapes at each nesting level
"""
def
helper
(
data
):
if
not
isinstance
(
data
,
(
list
,
tuple
)):
if
isinstance
(
data
,
ndarray
):
return
len
(
data
.
shape
),
type
(
data
).
__name__
+
" of "
+
str
(
data
.
dtype
)
+
" with shape "
+
str
(
data
.
shape
)
return
0
,
type
(
data
).
__name__
if
include_numpy_nesting
:
nesting
=
len
(
data
.
shape
)
else
:
nesting
=
0
return
nesting
,
[
type
(
data
).
__name__
+
" of "
+
str
(
data
.
dtype
)
+
" with shape "
+
str
(
data
.
shape
)]
return
0
,
[
type
(
data
).
__name__
]
else
:
result
=
type
(
data
).
__name__
result
+=
" [{}]"
.
format
(
len
(
data
))
result
=
[
f
"
{
type
(
data
).
__name__
}
[
{
len
(
data
)
}
]"
]
if
len
(
data
)
>
0
:
child
=
data
[
0
]
child_nesting
,
child_result
=
helper
(
child
)
result
+
=
" of "
+
child_result
result
=
result
+
child_result
else
:
child_nesting
=
0
return
(
child_nesting
+
1
),
result
nesting
,
result
=
helper
(
data
)
return
nesting
,
result
def
describe_data_shape
(
data
):
"""
Describe shape of data in human-readable form.
Returns string.
Can be used for debugging or for displaying information to user.
Note: this method inspects only first element of each list/tuple,
expecting they are all homogenous (that is usually true in Sverchok).
describe_data_shape(None) == 'Level 0: NoneType'
describe_data_shape(1) == 'Level 0: int'
describe_data_shape([]) == 'Level 1: list [0]'
describe_data_shape([1]) == 'Level 1: list [1] of int'
describe_data_shape([[(1,2,3)]]) == 'Level 3: list [1] of list [1] of tuple [3] of int'
"""
nesting
,
descriptions
=
describe_data_shape_by_level
(
data
)
result
=
" of "
.
join
(
descriptions
)
return
"Level {}: {}"
.
format
(
nesting
,
result
)
def
describe_data_structure
(
data
,
data_types
=
SIMPLE_DATA_TYPES
):
...
...
@@ -1285,7 +1333,10 @@ def get_other_socket(socket):
if
not
socket
.
is_linked
:
return
None
if
not
socket
.
is_output
:
other
=
socket
.
links
[
0
].
from_socket
if
socket
.
links
:
other
=
socket
.
links
[
0
].
from_socket
else
:
return
None
else
:
other
=
socket
.
links
[
0
].
to_socket
...
...
docs/nodes/list_struct/levels.rst
0 → 100644
Просмотр файла @
415efa52
List Levels
===========
Functionality
-------------
This node allows the user to manipulate with nesting structure of data by setting checkboxes. It allows to:
* Remove a level of nesting by concatenating nested lists of some level, or
* Add a level of nesting by adding another pair of square brackets around nested list of some level, or
* do both things at the same or at different nesting levels.
This node works with nested lists or tuples. Numpy arrays are considered to be atomic objects.
Inputs
------
This node has the following input:
* **Data**. Input data. This node supports data of any standard type (numbers,
vertices, surfaces and so on), with arbitrary nesting level. This input is
mandatory.
Parameters
----------
When **Data** input is connected, the interface of the node presents a table.
Each row of the table describes one nesting level of input data, and defines
what do you want to do with data at this nesting level. The table has the
following columns:
* **Depth**. This shows the nesting depth of this level, i.e. how deeply nested
this data is, counting from the outermost list. Outermost list always has
depth of 0, one that is nested in it has depth of 1, and so on.
* **Nesting**. This shows how many nesting levels are inside each item of data
at this level. At the innermost nesting level, each item of the list is an
"atomic object", for example it can be integer number, floating-point number,
surface or curve, and so on, but not a list or tuple. So, the innermost data
level has nesting level equal to 0 (zero). A list which consists of atomic
objects has nesting level of 1, and so on.
* **Shape**. This describes the shape of data at this level. For lists or
tuples, it shows whether this is a list or tuple, and also the number of
items in it, in square brackets. For atomic objects, it shows the type of the
data ("float", or "int", or "SvSurface", and so on).
* **Flatten**. This column contains a checkbox. If checked, the node will
concatenate all lists contained in list at this nesting level. Obviously,
atomic objects (nesting of 0) do not contain any nested objects, so for the
innermost level this checkbox is not available. For lists that contain atomic
objects (nesting of 1), this checkbox is not available either, as there are
no nested lists too. This checkbox does transform data only at one level, it
does not "go deeper" automatically. So, if you check this checkbox, you
always decrease nesting level of whole data by 1. To give some examples,
* ``[[1, 2], [3, 4]]`` is transformed into ``[1, 2, 3, 4]``.
* ``[[[1], [2]], [[3], [4]]]`` is transformed into ``[[1], [2], [3], [4]]``.
* **Wrap**. This column contains a checkbox. If checked, the node will put the
data at this nesting level in a separate list, i.e. wrap it in additional
pair of square brackets. So, by checking this checkbox, you always increase
the nesting level of whole data by 1. For example, if you check this
parameter at the innermost level (nesting of 0), the node will create a
separate list for each atomic object (wrap each atomic object into a list).
For simple shapes of data, many combinations of checkboxes will give identical
results; but for more deeply nested data, or when having more items at
outermost levels, there will be more different options. You can also connect
several "List Levels" nodes to do even more complex manipulations with data
structure.
Examples of Usage
-----------------
By default, all checkboxes are disabled, so the node does nothing:
.. image:: https://user-images.githubusercontent.com/284644/101237916-558f9b80-36fe-11eb-9240-25c0cf25c0c3.png
Let's wrap each number into a separate list (this is what "Graft" option of output socket menus does as well):
.. image:: https://user-images.githubusercontent.com/284644/101237917-56c0c880-36fe-11eb-8b0f-2caed2f5bcdb.png
By enabling "Wrap" at the next level, we put each vertex into a separate list:
.. image:: https://user-images.githubusercontent.com/284644/101237918-57595f00-36fe-11eb-9ddf-a7d456f0f985.png
The next level - put each list of vertices (object) into a separate list:
.. image:: https://user-images.githubusercontent.com/284644/101237919-57f1f580-36fe-11eb-937c-362b336de9c3.png
And the outermost level - put the whole data structure into additional pair of square brackets:
.. image:: https://user-images.githubusercontent.com/284644/101237920-57f1f580-36fe-11eb-9f64-1c06d3831efe.png
By enabling "Flatten" at the deepest available level, we concatenate vertices data into lists of numbers:
.. image:: https://user-images.githubusercontent.com/284644/101237921-588a8c00-36fe-11eb-9dd5-cf30a7701ac7.png
By flattening at the outermost level, we concatenate lists of vertices into a single list of vertices:
.. image:: https://user-images.githubusercontent.com/284644/101237921-588a8c00-36fe-11eb-9dd5-cf30a7701ac7.png
If we enable both Flatten flags, we concatenate lists of vertices into lists of numbers, AND we concatenate lists of numbers into a single list of numbers:
.. image:: https://user-images.githubusercontent.com/284644/101238132-f0d54080-36ff-11eb-99aa-d351bfb7f31e.png
docs/nodes/list_struct/list_struct_index.rst
Просмотр файла @
415efa52
...
...
@@ -17,3 +17,5 @@ List Struct
sort
split
start_end
levels
index.md
Просмотр файла @
415efa52
...
...
@@ -453,6 +453,7 @@
ListShuffleNode
SvListSortNode
ListFlipNode
SvListLevelsNode
## Dictionary
SvDictionaryIn
...
...
nodes/list_struct/levels.py
0 → 100644
Просмотр файла @
415efa52
# ##### 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
,
IntProperty
,
StringProperty
,
CollectionProperty
from
sverchok.node_tree
import
SverchCustomTreeNode
from
sverchok.data_structure
import
updateNode
,
describe_data_shape_by_level
,
list_levels_adjust
,
throttle_and_update_node
,
SIMPLE_DATA_TYPES
from
sverchok.utils.curve.core
import
SvCurve
from
sverchok.utils.surface.core
import
SvSurface
from
sverchok.dependencies
import
FreeCAD
ALL_TYPES
=
SIMPLE_DATA_TYPES
+
(
SvCurve
,
SvSurface
)
if
FreeCAD
is
not
None
:
import
Part
ALL_TYPES
=
ALL_TYPES
+
(
Part
.
Shape
,)
class
SvNestingLevelEntry
(
bpy
.
types
.
PropertyGroup
):
def
update_entry
(
self
,
context
):
if
hasattr
(
context
,
'node'
):
updateNode
(
context
.
node
,
context
)
else
:
info
(
"Node is not defined in this context, so will not update the node."
)
description
:
StringProperty
(
options
=
{
'SKIP_SAVE'
},
default
=
"?"
)
flatten
:
BoolProperty
(
name
=
"Flatten"
,
description
=
"Concatenate all child lists into one list"
,
default
=
False
,
update
=
update_entry
)
wrap
:
BoolProperty
(
name
=
"Wrap"
,
description
=
"Wrap data into additional pair of square brackets []"
,
default
=
False
,
update
=
update_entry
)
class
SvListLevelsNode
(
bpy
.
types
.
Node
,
SverchCustomTreeNode
):
'''
Triggers: List Levels
Tooltip: List nesting levels manipulation
'''
bl_idname
=
'SvListLevelsNode'
bl_label
=
'List Levels'
bl_icon
=
'OUTLINER'
levels_config
:
CollectionProperty
(
type
=
SvNestingLevelEntry
)
prev_nesting_level
:
IntProperty
(
default
=
0
,
options
=
{
'SKIP_SAVE'
})
def
draw_buttons
(
self
,
context
,
layout
):
n
=
len
(
self
.
levels_config
)
if
not
n
:
layout
.
label
(
text
=
"No data passed"
)
return
grid
=
layout
.
grid_flow
(
row_major
=
True
,
columns
=
5
,
align
=
True
)
grid
.
label
(
text
=
'Depth'
)
grid
.
label
(
text
=
'Nesting'
)
grid
.
label
(
text
=
'Shape'
)
grid
.
label
(
text
=
'Flatten'
)
grid
.
label
(
text
=
'Wrap'
)
for
i
,
entry
in
enumerate
(
self
.
levels_config
):
nesting
=
n
-
i
-
1
level_str
=
str
(
i
)
if
i
==
0
:
level_str
+=
" (outermost)"
elif
nesting
==
0
:
level_str
+=
" (innermost)"
grid
.
label
(
text
=
level_str
)
grid
.
label
(
text
=
str
(
nesting
))
grid
.
label
(
text
=
entry
.
description
)
if
nesting
<
2
:
grid
.
label
(
icon
=
'X'
,
text
=
''
)
else
:
grid
.
prop
(
entry
,
'flatten'
,
text
=
''
)
grid
.
prop
(
entry
,
'wrap'
,
text
=
''
)
def
sv_update
(
self
):
self
.
update_ui
(
False
)
#@throttle_and_update_node
def
update_ui
(
self
,
update_during_process
):
try
:
data
=
self
.
inputs
[
'Data'
].
sv_get
(
default
=
[])
except
LookupError
:
data
=
[]
if
not
data
:
self
.
levels_config
.
clear
()
return
nesting
,
descriptions
=
describe_data_shape_by_level
(
data
,
include_numpy_nesting
=
False
)
rebuild_list
=
self
.
prev_nesting_level
!=
nesting
self
.
prev_nesting_level
=
nesting
if
rebuild_list
:
self
.
levels_config
.
clear
()
for
descr
in
descriptions
:
self
.
levels_config
.
add
().
description
=
descr
else
:
for
entry
,
descr
in
zip
(
self
.
levels_config
,
descriptions
):
entry
.
description
=
descr
def
sv_init
(
self
,
context
):
self
.
width
=
300
self
.
inputs
.
new
(
'SvStringsSocket'
,
'Data'
)
self
.
outputs
.
new
(
'SvStringsSocket'
,
'Data'
)
def
process
(
self
):
if
not
self
.
inputs
[
'Data'
].
is_linked
:
return
self
.
update_ui
(
True
)
if
not
self
.
outputs
[
'Data'
].
is_linked
:
return
data
=
self
.
inputs
[
'Data'
].
sv_get
(
default
=
[])
result
=
list_levels_adjust
(
data
,
self
.
levels_config
,
data_types
=
ALL_TYPES
)
self
.
outputs
[
'Data'
].
sv_set
(
result
)
classes
=
[
SvNestingLevelEntry
,
SvListLevelsNode
]
def
register
():
for
name
in
classes
:
bpy
.
utils
.
register_class
(
name
)
def
unregister
():
for
name
in
reversed
(
classes
):
bpy
.
utils
.
unregister_class
(
name
)
tests/data_structure_tests.py
Просмотр файла @
415efa52
...
...
@@ -99,13 +99,90 @@ class DataStructureTests(SverchokTestCase):
output
=
rotate_list
(
input
,
2
)
self
.
assertEquals
(
output
,
expected_output
)
def
test_describe_data_shape
(
self
):
def
test_describe_data_shape
_1
(
self
):
self
.
subtest_assert_equals
(
describe_data_shape
(
None
),
'Level 0: NoneType'
)
self
.
subtest_assert_equals
(
describe_data_shape
(
1
),
'Level 0: int'
)
self
.
subtest_assert_equals
(
describe_data_shape
([]),
'Level 1: list [0]'
)
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'
)
def
test_describe_data_shape_2
(
self
):
nesting
,
descriptions
=
describe_data_shape_by_level
([[(
1
,
2
,
3
)]])
expected_nesting
=
3
expected_descriptions
=
[
"list [1]"
,
"list [1]"
,
"tuple [3]"
,
"int"
]
self
.
subtest_assert_equals
(
nesting
,
expected_nesting
)
self
.
subtest_assert_equals
(
descriptions
,
expected_descriptions
)
def
test_adjust_1
(
self
):
instructions
=
[
SvListLevelAdjustment
(
wrap
=
True
)]
input_data
=
1
result
=
list_levels_adjust
(
input_data
,
instructions
)
expected_result
=
[
1
]
self
.
assert_sverchok_data_equal
(
result
,
expected_result
)
def
test_adjust_2
(
self
):
instructions
=
[
SvListLevelAdjustment
(
wrap
=
True
),
SvListLevelAdjustment
()]
input_data
=
[
1
]
result
=
list_levels_adjust
(
input_data
,
instructions
)
expected_result
=
[[
1
]]
self
.
assert_sverchok_data_equal
(
result
,
expected_result
)
def
test_adjust_3
(
self
):
instructions
=
[
SvListLevelAdjustment
(
wrap
=
True
),
SvListLevelAdjustment
()]
input_data
=
[
1
,
2
]
result
=
list_levels_adjust
(
input_data
,
instructions
)
expected_result
=
[[
1
,
2
]]
self
.
assert_sverchok_data_equal
(
result
,
expected_result
)
def
test_adjust_4
(
self
):
instructions
=
[
SvListLevelAdjustment
(),
SvListLevelAdjustment
(
wrap
=
True
)]
input_data
=
[
1
,
2
]
result
=
list_levels_adjust
(
input_data
,
instructions
)
expected_result
=
[[
1
],
[
2
]]
self
.
assert_sverchok_data_equal
(
result
,
expected_result
)
def
test_adjust_5
(
self
):
instructions
=
[
SvListLevelAdjustment
(
flatten
=
True
),
SvListLevelAdjustment
()]
input_data
=
[
1
]
result
=
list_levels_adjust
(
input_data
,
instructions
)
expected_result
=
[
1
]
self
.
assert_sverchok_data_equal
(
result
,
expected_result
)
def
test_adjust_6
(
self
):
instructions
=
[
SvListLevelAdjustment
(
flatten
=
True
),
SvListLevelAdjustment
(),
SvListLevelAdjustment
()]
input_data
=
[[
1
],
[
2
]]
result
=
list_levels_adjust
(
input_data
,
instructions
)
expected_result
=
[
1
,
2
]
self
.
assert_sverchok_data_equal
(
result
,
expected_result
)
def
test_adjust_7
(
self
):
instructions
=
[
SvListLevelAdjustment
(),
SvListLevelAdjustment
(
wrap
=
True
),
SvListLevelAdjustment
()]
input_data
=
[[
1
],
[
2
]]
result
=
list_levels_adjust
(
input_data
,
instructions
)
expected_result
=
[[[
1
]],
[[
2
]]]
self
.
assert_sverchok_data_equal
(
result
,
expected_result
)
def
test_adjust_8
(
self
):
instructions
=
[
SvListLevelAdjustment
(
flatten
=
True
),
SvListLevelAdjustment
(
wrap
=
True
),
SvListLevelAdjustment
()]
input_data
=
[[
1
],
[
2
]]
result
=
list_levels_adjust
(
input_data
,
instructions
)
expected_result
=
[[
1
],
[
2
]]
self
.
assert_sverchok_data_equal
(
result
,
expected_result
)
def
test_adjust_9
(
self
):
instructions
=
[
SvListLevelAdjustment
(
flatten
=
True
),
SvListLevelAdjustment
(),
SvListLevelAdjustment
()]
input_data
=
[[
1
,
2
],
[
3
,
4
]]
result
=
list_levels_adjust
(
input_data
,
instructions
)
expected_result
=
[
1
,
2
,
3
,
4
]
self
.
assert_sverchok_data_equal
(
result
,
expected_result
)
def
test_adjust_10
(
self
):
instructions
=
[
SvListLevelAdjustment
(
flatten
=
True
,
wrap
=
True
),
SvListLevelAdjustment
(),
SvListLevelAdjustment
()]
input_data
=
[[
1
,
2
],
[
3
,
4
]]
result
=
list_levels_adjust
(
input_data
,
instructions
)
expected_result
=
[[
1
,
2
,
3
,
4
]]
self
.
assert_sverchok_data_equal
(
result
,
expected_result
)
def
test_flatten_1
(
self
):
data
=
[[
1
,
2
],
[
3
,
4
]]
result
=
flatten_data
(
data
)
...
...
Редактирование
Предварительный просмотр
Поддерживает Markdown
0%
Попробовать снова
или
прикрепить новый файл
.
Отмена
You are about to add
0
people
to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Отмена
Пожалуйста,
зарегистрируйтесь
или
войдите
чтобы прокомментировать