Skip to content

Commit

Permalink
Fix list levels node (#4190)
Browse files Browse the repository at this point in the history
* fixes #4184 + UI refactoring
  • Loading branch information
Durman authored Aug 31, 2022
1 parent 7835e38 commit 746c23b
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 99 deletions.
30 changes: 12 additions & 18 deletions docs/nodes/list_struct/levels.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,18 @@ 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
* **Level**. 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.
depth of 1, one that is nested in it has depth of 2, 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
atomic objects (integers, floats etc.) 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
objects, 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,
Expand All @@ -72,33 +66,33 @@ Examples of Usage

By default, all checkboxes are disabled, so the node does nothing:

.. image:: https://user-images.githubusercontent.com/9460236/129511959-8e17fe20-ce5e-4127-a88b-bb1dc84b916e.png
.. image:: https://user-images.githubusercontent.com/28003269/187598033-b1489f12-a949-4a14-842c-b77b4d1a94c0.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/9460236/129511970-6767616b-7f4f-4672-85d8-2ecf96cc4111.png
.. image:: https://user-images.githubusercontent.com/28003269/187598129-4cd1cb55-4122-43dd-b175-d5ed36b353d9.png

By enabling "Wrap" at the next level, we put each vertex into a separate list:

.. image:: https://user-images.githubusercontent.com/9460236/129513146-33f5e2c7-345b-414e-bbf6-561b3dacabd7.png
.. image:: https://user-images.githubusercontent.com/28003269/187598191-b9da1499-c19b-46b4-8564-6e548ca2a2a0.png

The next level - put each list of vertices (object) into a separate list:

.. image:: https://user-images.githubusercontent.com/9460236/129511986-c4bf1bac-f8a6-44a9-b187-b532210f89f8.png
.. image:: https://user-images.githubusercontent.com/28003269/187598252-75720f20-48a9-4760-8c97-661867e9843a.png

And the outermost level - put the whole data structure into additional pair of square brackets:

.. image:: https://user-images.githubusercontent.com/9460236/129511989-bf1b69d4-b916-4771-a289-30d0761cf60c.png
.. image:: https://user-images.githubusercontent.com/28003269/187598332-9e6ef1a8-80de-4ca4-9991-659c24c6fdc9.png

By enabling "Flatten" at the deepest available level, we concatenate vertices data into lists of numbers:

.. image:: https://user-images.githubusercontent.com/9460236/129511997-5fe4d9bd-ce06-40cc-811e-f41de1ec3378.png
.. image:: https://user-images.githubusercontent.com/28003269/187598388-c978e176-e697-4535-ba5b-c7e7612182d4.png

By flattening at the outermost level, we concatenate lists of vertices into a single list of vertices:

.. image:: https://user-images.githubusercontent.com/9460236/129512002-d194d402-d80f-4a4f-a3e6-4afb281fd191.png
.. image:: https://user-images.githubusercontent.com/28003269/187598453-09121868-9fc0-4078-90f9-21d5dc50a40c.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/9460236/129512012-38d5314a-d799-4fb5-ad50-b6792e2907e4.png
.. image:: https://user-images.githubusercontent.com/28003269/187598519-c849fde8-352a-43a5-b638-787e0e9d425c.png

2 changes: 1 addition & 1 deletion index.md
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@
ListShuffleNode
SvListSortNode
ListFlipNode
SvListLevelsNode
SvListLevelsNodeMK2

## Dictionary
SvDictionaryIn
Expand Down
132 changes: 52 additions & 80 deletions nodes/list_struct/levels.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,29 @@
# ##### END GPL LICENSE BLOCK #####

import bpy
from bpy.props import BoolProperty, IntProperty, StringProperty, CollectionProperty, BoolVectorProperty
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, SIMPLE_DATA_TYPES,\
changable_sockets
from sverchok.data_structure import (updateNode, describe_data_shape_by_level, list_levels_adjust, SIMPLE_DATA_TYPES,
changable_sockets)
from sverchok.utils.curve.core import SvCurve
from sverchok.utils.surface.core import SvSurface
from sverchok.dependencies import FreeCAD
from sverchok.utils.logging import info
from sverchok.utils.logging import debug
from sverchok.utils.handle_blender_data import correct_collection_length

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):

class SvNestingLevelEntryMK2(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.")
debug("Node is not defined in this context, so will not update the node.")

description : StringProperty(options = {'SKIP_SAVE'}, default="?")
flatten : BoolProperty(
Expand All @@ -50,105 +53,74 @@ def update_entry(self, context):
default=False,
update=update_entry)

class SvListLevelsNode(bpy.types.Node, SverchCustomTreeNode):

class SvListLevelsNodeMK2(SverchCustomTreeNode, bpy.types.Node):
'''
Triggers: List Levels
Tooltip: List nesting levels manipulation
'''
bl_idname = 'SvListLevelsNode'
bl_idname = 'SvListLevelsNodeMK2'
bl_label = 'List Levels'
bl_icon = 'OUTLINER'

levels_config : CollectionProperty(type=SvNestingLevelEntry)
prev_nesting_level : IntProperty(default = 0, options = {'SKIP_SAVE'})
flatten_mem: BoolVectorProperty(size=32, default=[False for i in range(32)])
wrap_mem: BoolVectorProperty(size=32, default=[False for i in range(32)])
levels_config : CollectionProperty(type=SvNestingLevelEntryMK2)
nesting: IntProperty(description="How much nested levels should be shown")

def draw_buttons(self, context, layout):
n = len(self.levels_config)
if not n:
if not self.nesting:
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)

# https://blender.stackexchange.com/questions/51256/how-to-create-uilist-with-auto-aligned-three-columns/51263#51263
lvl_split = 0.08
shape_split = 0.6
flat_split = 0.5

col = layout.column()
col.label(text='Lvl|Shape|Flatten|Wrap')

for i, entry in zip(range(self.nesting), self.levels_config):
nesting = self.nesting - i - 1
row = col.split(factor=lvl_split)
row.label(text=f"{i+1}")
row = row.split(factor=shape_split)
row.label(text=entry.description)
row = row.split(factor=flat_split)
if nesting < 2:
grid.label(icon='X', text='')
row.label(icon='X', text='')
else:
grid.prop(entry, 'flatten', text='')
grid.prop(entry, 'wrap', text='')
row.prop(entry, 'flatten', text='')
row.prop(entry, 'wrap', text='')

def sv_init(self, context):
self.inputs.new('SvStringsSocket', 'Data')
self.outputs.new('SvStringsSocket', 'Data')

def sv_update(self):
self.update_buttons(False)

def update_buttons(self, update_during_process):
try:
data = self.inputs['Data'].sv_get(default=[])
except LookupError:
data = []
if not data:
self.prev_nesting_level = 0
for i, l in enumerate(self.levels_config):
self.flatten_mem[i] = l.flatten
self.wrap_mem[i] = l.wrap

self.levels_config.clear()
return
changable_sockets(self, 'Data', ['Data', ])

def process(self):
data = self.inputs['Data'].sv_get(default=[], deepcopy=False)

changable_sockets(self, 'Data', ['Data', ])
nesting, descriptions = describe_data_shape_by_level(data, include_numpy_nesting=False)
rebuild_list = self.prev_nesting_level != nesting
self.prev_nesting_level = nesting
nesting += 1
self.nesting = nesting if data else 0
if len(self.levels_config) < nesting:
correct_collection_length(self.levels_config, nesting)

if rebuild_list:
self.levels_config.clear()
for i, descr in enumerate(descriptions):
l = self.levels_config.add()
l.description = descr
l.flatten = self.flatten_mem[i]
l.wrap = self.wrap_mem[i]
for entry, description in zip(self.levels_config, descriptions):
entry.description = description

if data:
result = list_levels_adjust(data, self.levels_config, data_types=ALL_TYPES)
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 sv_copy(self, original):
self.prev_nesting_level = 0
result = []

def process(self):
if not self.inputs['Data'].is_linked:
return
self.update_buttons(True)
if not self.outputs['Data'].is_linked:
return
self.outputs['Data'].sv_set(result)

data = self.inputs['Data'].sv_get(default=[], deepcopy=False)
result = list_levels_adjust(data, self.levels_config, data_types=ALL_TYPES)

self.outputs['Data'].sv_set(result)
classes = [SvNestingLevelEntryMK2, SvListLevelsNodeMK2]

classes = [SvNestingLevelEntry, SvListLevelsNode]

def register():
for name in classes:
Expand Down
Loading

0 comments on commit 746c23b

Please sign in to comment.