Коммит bf3d8ae4 создал по автору durman's avatar durman
Просмотр файлов

Merge remote-tracking branch 'origin/master' into fixing_old_nodes_register

# Conflicts:
#	core/__init__.py
#	core/handlers.py
#	old_nodes/__init__.py
владельцы 85aa264e 9a39b998
......@@ -96,6 +96,7 @@ def register():
def unregister():
sverchok.utils.clear_node_classes()
sv_registration_utils.unregister_all(imported_modules + node_list)
sv_registration_utils.unregister_all(imported_modules)
sv_registration_utils.unregister_all(node_list)
# EOF
......@@ -11,10 +11,10 @@ root_modules = [
]
core_modules = [
"monad_properties", "sv_custom_exceptions",
"sv_custom_exceptions",
"node_id_dict", "links", "sockets",
"handlers", "update_system",
"monad", "events", "node_group", "group_handlers"
"events", "node_group", "group_handlers"
]
def sv_register_modules(modules):
......@@ -28,7 +28,6 @@ def sv_unregister_modules(modules):
clear_system_cache()
for m in reversed(modules):
if hasattr(m, "unregister"):
# print("Unregistering module: {}".format(m.__name__))
try:
m.unregister()
except RuntimeError as e:
......
......@@ -57,7 +57,6 @@ class GroupEvent:
class BlenderEventsTypes(Enum):
tree_update = auto() # this updates is calling last with exception of creating new node
monad_tree_update = auto()
node_update = auto() # it can be called last during creation new node event
add_node = auto() # it is called first in update wave
copy_node = auto() # it is called first in update wave
......
......@@ -39,11 +39,9 @@ def sverchok_trees():
if ng.bl_idname == 'SverchCustomTreeType':
yield ng
def update_all_monads_found():
for monad in (ng for ng in bpy.data.node_groups if ng.bl_idname == 'SverchGroupTreeType'):
if monad.input_node and monad.output_node:
monad.update_cls()
def get_all_sverchok_affiliated_trees():
sv_types = {'SverchCustomTreeType', 'SvGroupTree'}
return list(ng for ng in bpy.data.node_groups if ng.bl_idname in sv_types and ng.nodes)
def has_frame_changed(scene):
......@@ -190,8 +188,6 @@ def sv_post_load(scene):
# ensure current nodeview view scale / location parameters reflect users' system settings
node_tree.SverchCustomTree.update_gl_scale_info(None, "sv_post_load")
update_all_monads_found()
# register and mark old and dependent nodes
with catch_log_error():
if any(not n.is_registered_node_type() for ng in BlTrees().sv_trees for n in ng.nodes):
......
Это отличие свёрнуто
# 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 sys
import re
import bpy
from bpy.types import PropertyGroup
from bpy.props import (
FloatProperty, FloatVectorProperty, IntProperty, StringProperty, EnumProperty)
class PropsBase:
# ignored /internal names
internal_names = {"prop_name", "attr", "update", "bl_rna", "rna_type"}
prop_name: StringProperty(description="Internal name")
def get_settings(self):
settings = {k:v for k, v in self.items() if k not in self.internal_names}
return settings
def set_settings(self, settings):
for key, value in settings.items():
setattr(self, key, value)
def from_socket(self, socket):
p_type, p_dict = getattr(socket.node.rna_type, socket.prop_name)
self.set_settings(p_dict)
self.prop_name = socket.prop_name
def draw(self, context, layout):
"""
names = [name for name in dir(self) if not name in self.internal_names and not name.startswith("__")]
for name in sorted(names):
layout.prop(self, name)
"""
layout.prop(self, "default")
layout.prop(self, "soft_min")
layout.prop(self, "soft_max")
layout.prop(self, "description")
# FloatProperty
'''
bpy.props.FloatProperty(name="", description="", default=0.0,
min=sys.float_info.min, max=sys.float_info.max,
soft_min=sys.float_info.min, soft_max=sys.float_info.max,
step=3, precision=2, options={'ANIMATABLE'}, subtype='NONE',
unit='NONE', update=None, get=None, set=None)
subtype (string) – Enumerator in ['PIXEL', 'UNSIGNED', 'PERCENTAGE', 'FACTOR', 'ANGLE', 'TIME', 'DISTANCE', 'NONE'].
unit (string) – Enumerator in ['NONE', 'LENGTH', 'AREA', 'VOLUME', 'ROTATION', 'TIME', 'VELOCITY', 'ACCELERATION'].
'''
unit_items = (
('NONE', 'NONE', 'NONE', 0),
('LENGTH', 'LENGTH', 'LENGTH', 1),
('AREA', 'AREA', 'AREA', 2),
('VOLUME', 'VOLUME', 'VOLUME', 3),
('ROTATION', 'ROTATION', 'ROTATION', 4),
('TIME', 'TIME', 'TIME', 5),
('VELOCITY', 'VELOCITY', 'VELOCITY', 6),
('ACCELERATION', 'ACCELERATION', 'ACCELERATION', 7)
)
float_items = (
('PIXEL', 'PIXEL', 'PIXEL', 0),
('UNSIGNED', 'UNSIGNED', 'UNSIGNED', 1),
('PERCENTAGE', 'PERCENTAGE', 'PERCENTAGE', 2),
('FACTOR', 'FACTOR', 'FACTOR', 3),
('ANGLE', 'ANGLE', 'ANGLE', 4),
('TIME', 'TIME', 'TIME', 5),
('DISTANCE', 'DISTANCE', 'DISTANCE', 6),
('NONE', 'NONE', 'NONE', 7)
)
class SvFloatPropertySettingsGroup(PropertyGroup, PropsBase):
def get_settings(self):
settings = super().get_settings()
# okay this code could perhaps be more clever
# but protects from gettings the index of the EnumProperty
# instead of the appropriate string value
if "subtype" in settings:
ref_nr = settings['subtype']
for item in float_items:
if ref_nr == item[-1]:
settings['subtype'] = item[0]
break
if "unit" in settings:
ref_nr = settings['unit']
for item in unit_items:
if ref_nr == item[-1]:
settings['unit'] = item[0]
break
return settings
name: StringProperty(description="Show name")
description: StringProperty()
default: FloatProperty(default=0.0)
min: FloatProperty(default=sys.float_info.min)
max: FloatProperty(default=sys.float_info.max)
soft_min: FloatProperty(default=sys.float_info.min)
soft_max: FloatProperty(default=sys.float_info.max)
step: IntProperty(default=3)
precision: IntProperty(default=2)
subtype: EnumProperty(items=float_items, name="Subtype", default='NONE')
unit: EnumProperty(items=unit_items, name="Unit", default='NONE')
# INT PROPERTY
'''
bpy.props.IntProperty(name="", description="",
default=0, min=-2**31, max=2**31-1,
soft_min=-2**31, soft_max=2**31-1,
step=1, options={'ANIMATABLE'},
subtype='NONE', update=None, get=None, set=None)
Returns a new int property definition.
Parameters:
name (string) – Name used in the user interface.
description (string) – Text used for the tooltip and api documentation.
min (int) – Hard minimum, trying to assign a value below will silently assign this minimum instead.
max (int) – Hard maximum, trying to assign a value above will silently assign this maximum instead.
soft_max (int) – Soft maximum (<= max), user won’t be able to drag the widget above this value in the UI.
soft_min (int) – Soft minimum (>= min), user won’t be able to drag the widget below this value in the UI.
step (int) – Step of increment/decrement in UI, in [1, 100], defaults to 1 (WARNING: unused currently!).
options (set) – Enumerator in [‘HIDDEN’, ‘SKIP_SAVE’, ‘ANIMATABLE’, ‘LIBRARY_EDITABLE’, ‘PROPORTIONAL’,’TEXTEDIT_UPDATE’].
subtype (string) – Enumerator in [‘PIXEL’, ‘UNSIGNED’, ‘PERCENTAGE’, ‘FACTOR’, ‘ANGLE’, ‘TIME’, ‘DISTANCE’, ‘NONE’].
update (function) – Function to be called when this value is modified, This function must take 2 values (self, context) and return None. Warning there are no safety checks to avoid infinite recursion.
get (function) – Function to be called when this value is ‘read’, This function must take 1 value (self) and return the value of the property.
set (function) – Function to be called when this value is ‘written’, This function must take 2 values (self, value) and return None.
'''
int_subtypes = [
('PIXEL', 'PIXEL', 'PIXEL', 0),
('UNSIGNED', 'UNSIGNED', 'UNSIGNED', 1),
('PERCENTAGE', 'PERCENTAGE', 'PERCENTAGE', 2),
('FACTOR', 'FACTOR', 'FACTOR', 3),
('ANGLE', 'ANGLE', 'ANGLE', 4),
('TIME', 'TIME', 'TIME', 5),
('DISTANCE', 'DISTANCE', 'DISTANCE', 6),
('NONE', 'NONE', 'NONE', 7)
]
class SvIntPropertySettingsGroup(PropertyGroup, PropsBase):
def get_settings(self):
# okay this code could perhaps be more clever
# but protects from gettings the index of the EnumProperty
# instead of the appropriate string value
settings = super().get_settings()
if "subtype" in settings:
ref_nr = settings['subtype']
for item in int_subtypes:
if ref_nr == item[-1]:
settings['subtype'] = item[0]
break
return settings
name: StringProperty(description="Show name")
description: StringProperty()
default: IntProperty(default=0)
min: IntProperty(default=-2**31)
max: IntProperty(default=2**31-1)
soft_min: IntProperty(default=-2**31)
soft_max: IntProperty(default=2**31-1)
step: IntProperty(default=1) # not used
subtype: EnumProperty(items=float_items, name="Subtype", default='NONE')
def find_hightest_num_in_matching_params(monad_prop_names, kind):
regex = "^(?P<kind>ints|floats)_(?P<number>\d+)_(?P<variable>\S+)$"
nums = [1]
for name in monad_prop_names:
matches = re.search(regex, name)
if matches and matches.group('kind') == kind:
nums.append(int(matches.group('number')))
return max(nums)
def ensure_unique(monad_prop_names, new_prop_name):
# can be used unchanged
if not (new_prop_name in monad_prop_names):
return new_prop_name
print(f"{new_prop_name} (prop_name) is used.. making a new one")
# find the highest token and return it plus one
kind, num, variable = new_prop_name.split('_', 2)
num = find_hightest_num_in_matching_params(monad_prop_names, kind)
proposed_new_name = f"{kind}_{int(num) + 1}_{variable}"
print(f"new prop_name {proposed_new_name}")
return proposed_new_name
classes = [
SvFloatPropertySettingsGroup,
SvIntPropertySettingsGroup
]
def register():
for class_name in classes:
bpy.utils.register_class(class_name)
def unregister():
for class_name in reversed(classes):
bpy.utils.unregister_class(class_name)
......@@ -525,9 +525,6 @@ class AddGroupNode(PlacingNodeOperator, bpy.types.Operator):
return False, ''
tree = path.node_tree
if tree.bl_idname == 'SverchCustomTreeType':
for node in tree.nodes:
if node.bl_idname.startswith('SvGroupNodeMonad'):
return False, 'Either monad or group node should be used in the tree'
return True, 'Add group node'
elif tree.bl_idname == 'SvGroupTree':
return True, "Add group node"
......
......@@ -307,9 +307,6 @@ def do_update_heat_map(node_list, nodes):
nodes[name].color = cold.lerp(hot, t / t_max)
def update_error_nodes(ng, name, err=Exception):
if ng.bl_idname == "SverchGroupTreeType":
return # ignore error color inside of monad
if "error nodes" in ng:
error_nodes = ast.literal_eval(ng["error nodes"])
else:
......@@ -413,7 +410,6 @@ def do_update_general(node_list, nodes, procesed_nodes=set()):
exception("Node %s had exception: %s", node_name, err)
if hasattr(ng, "sv_show_error_in_tree"):
# not yet supported in monad trees..
if ng.sv_show_error_in_tree:
error_text = traceback.format_exc()
start_exception_drawing_with_bgl(ng, node_name, error_text, err)
......
......@@ -36,7 +36,7 @@ from numpy import (
tile as np_tile,
float64,
int32, int64)
from sverchok.utils.logging import info
from sverchok.utils.logging import info, debug
DEBUG_MODE = False
......@@ -1349,7 +1349,7 @@ def changable_sockets(node, inputsocketname, outputsocketname):
if not inputsocketname in node.inputs:
# - node not initialized in sv_init yet,
# - or socketname incorrect
info(f"changable_socket was called on {node.name} with a socket named {inputsocketname}, this socket does not exist")
debug(f"changable_socket was called on {node.name} with a socket named {inputsocketname}, this socket does not exist")
return
in_socket = node.inputs[inputsocketname]
......
Monad
=====
Functionality
-------------
Warning: This feature is a work in progress in 2.8+. Expect stuff to be broken and breakages during usage.
-- zeffii
This node encapsulates several selected nodes (Ctrl+G) into a single node, similar to a shadertree node group.
There are two parts to this node,
- (Monad) the outer node shell "Monad", this Node is dynamically defined with new properties and sockets whenever required by the Tree inside.
- (Tree) the internal node tree, with its own input and output nodes. Interaction with the input and output nodes will update the "Monad" shell.
When editing the Tree you can
- rearrange sockets
- rename sockets
- set slider limits
- change socket types
Parameters
----------
The UI hints at a few fundamental features of the Monad:
- Vectorize , Vectorize with Split
- Loop n times
*Vectorize*
tries to match incoming datastreams by repeating data in those sockets that don't have the same number of internal lists.
*Vectorize with Split*
The more complicated this gets, the harder it is to explain in text. Please see some examples below.
*Loop n times*
This can be used for repeatedly applying an effect to a mesh, the number of iteration is currently only configurable via the node UI - it's too easy to accidentally grind your computer to a halt if that was set dynamically via an input socket.
- currently you must connect all named sockets,
- the input and output nodes must be fully connected and the arrangement/order of sockets must be consistent
connected like this::
start yes no
_____________ _____________ _____________
|_input_node _| |_output node_| |_output node_|
| verts o =------ ---------= o verts | --------= o edges |
| edges o =-------- -------= o edges | ------= o faces |
| faces o =---------- -----= o faces | ----= o verts |
| - - | - |
|_____________| |_____________| |_____________|
Inputs and Outputs
------------------
Are entirely defined by the Tree that Monad encapsulates
Example of usage
----------------
The github issue tracker has many issues (question and answers) regarding this node, it's a good place to start.
I suggest reading this: https://github.com/nortikin/sverchok/issues/863
......@@ -13,7 +13,6 @@ Scene
curve_in
obj_remote_mk2
3dview_props
monad
collection_picker_mk1
particles_MK2
node_remote_mk2
......
......@@ -134,7 +134,6 @@ It can be good idea to store as a preset (and maybe share) the following things:
* Scripted node, or "Mesh Expression" node, or one of other nodes that use
Blender's text blocks as settings. Such nodes are stored together with
corresponding text.
* Group (monad) node. It is saved with all contents.
* Several linked nodes, that do some completed thing, for later usage "as-is".
* Several linked nodes, that are "sort of going to be needed in this
combination often", so that later you can start with this preset and add some
......@@ -385,14 +384,6 @@ Relax, most of these warnings can be ignored, unless the Tree fails to import, t
By all means if you like using this feature, file issues in `this thread <https://github.com/nortikin/sverchok/issues/422>`_. The best way to solve issues is to share with us a screenshot of the last few lines of the error if we need more then we will ask for a copy of the `.blend`.
Groups Panel
============
Crete a node group (Monad) from selection.
It can have vectorized inputs, adding or removing sockets.
Sverchok groups is a beta feature, use a your own risk and please report bugs. Also while it is in beta old node groups may break.
`Bug reports <https://github.com/nortikin/sverchok/issues/462>`_.
Templates in menu panel of nodes area
=====================================
......
......@@ -14,7 +14,7 @@ This is a collection of shortcuts useful in the Sverchok node tree, some are Ble
- 2: Mesh: Generators, Transforms, Modifiers and CAD
- 3: Advanced Objects: Curve, Surface, Field, Spatial, Pulga Physics, Alpha and Beta
- 4: Connection: Viz, Text, Scene, Exchange, BPY Data, Network and SVG
- 5: Sv Interface: Layout, Monad, Group and Presets
- 5: Sv Interface: Layout, Group and Presets
**Shift + S** - Add solids node menu (needs FreeCad)
......@@ -63,10 +63,6 @@ This is a collection of shortcuts useful in the Sverchok node tree, some are Ble
**F7** - Enables/Disables the Draft mode of the current node-tree
**Ctrl + G** - Create Monad (Group) and re-link.
**Ctrl + Shift + G** - Create Monad (Group).
**L** - Select Nodes that link to the previously selected nodes
**Shift + L** - Select Nodes that are linked from the previously selected nodes
......
Это отличие свёрнуто
......@@ -404,57 +404,6 @@ def draw_add_node_operator(layout, nodetype, label=None, icon_name=None, params=
return add
def sv_group_items(context):
"""
Based on the built in node_group_items in the blender distrubution
somewhat edited to fit.
"""
if context is None:
return
space = context.space_data
if not space:
return
ntree = space.edit_tree
if not ntree:
return
yield NodeItemCustom(draw=draw_node_ops)
def contains_group(nodetree, group):
if nodetree == group:
return True
else:
for node in nodetree.nodes:
if node.bl_idname in node_tree_group_type.values() and node.node_tree is not None:
if contains_group(node.node_tree, group):
return True
return False
if ntree.bl_idname == "SverchGroupTreeType":
yield NodeItem("SvMonadInfoNode", "Monad Info")
for monad in context.blend_data.node_groups:
if monad.bl_idname != "SverchGroupTreeType":
continue
# make sure class exists
cls_ref = get_node_class_reference(monad.cls_bl_idname)
if cls_ref and monad.cls_bl_idname:
yield NodeItem(monad.cls_bl_idname, monad.name)
elif monad.cls_bl_idname:
monad_cls_template_dict = {"cls_bl_idname": "str('{}')".format(monad.cls_bl_idname)}
yield NodeItem("SvMonadGenericNode", monad.name, monad_cls_template_dict)
def draw_node_ops(self,layout, context):
make_monad = "node.sv_monad_from_selected"
ungroup_monad = "node.sv_monad_expand"
update_import = "node.sv_monad_class_update"
layout.operator(make_monad, text='make group (+relink)', icon='RNA')
layout.operator(make_monad, text='make group', icon='RNA').use_relinking = False
layout.operator(ungroup_monad, text='ungroup', icon='RNA')
layout.operator(update_import, text='update appended/linked', icon='RNA')
layout.separator()
def strformated_tree(nodes):
......@@ -506,8 +455,6 @@ def make_categories():
logger.info(f"The following nodes are not enabled (probably due to missing dependancies)\n{strformated_tree(nodes_not_enabled)}")
node_categories.append(SverchNodeCategory("SVERCHOK_MONAD", "Monad", items=sv_group_items))
SverchNodeItem.new('SvMonadInfoNode')
return node_categories, node_count, original_categories
def register_node_panels(identifier, std_menu):
......
......@@ -41,7 +41,7 @@ from sverchok.utils.exception_drawing_with_bgl import clear_exception_drawing_wi
class SvNodeTreeCommon(object):
'''
Common methods shared between Sverchok node trees (normal and monad trees)
Common methods shared between Sverchok node trees
'''
# auto update toggle of the node tree
......@@ -61,28 +61,10 @@ class SvNodeTreeCommon(object):
self.tree_id_memory = str(hash(self) ^ hash(time.monotonic()))
return self.tree_id_memory
def get_groups(self):
"""
It gets monads of node tree,
Update them (the sv_update method will check if anything changed inside the monad
and will change the monad outputs in that case)
Return the monads that have changed (
to inform the caller function that the nodes downstream have to be updated with the new data)
"""
affected_groups =[]
for node in self.nodes:
if 'SvGroupNode' in node.bl_idname:
sub_tree = node.monad
sub_tree.sv_update()
if sub_tree.has_changed:
affected_groups.append(node)
sub_tree.has_changed = False
return affected_groups
def sv_update(self):
"""
the method checks if anything changed inside the normal tree or monad
and update them if necessary
the method checks if anything changed inside the tree
and update it if necessary
"""
self.sv_links.create_new_links(self)
if self.sv_links.links_have_changed(self):
......@@ -90,8 +72,6 @@ class SvNodeTreeCommon(object):
build_update_list(self)
process_from_nodes(self.sv_links.get_nodes(self))
self.sv_links.store_links_cache(self)
else:
process_from_nodes(self.get_groups())
def animation_update(self):
"""Find animatable nodes and update from them"""
......@@ -334,7 +314,7 @@ class UpdateNodes:
- sets node color
"""
ng = self.id_data
if ng.bl_idname in {'SverchCustomTreeType', 'SverchGroupTreeType'}:
if ng.bl_idname in {'SverchCustomTreeType', }:
ng.nodes_dict.load_node(self)
with ng.throttle_update():
try:
......@@ -361,7 +341,7 @@ class UpdateNodes:
s.sv_forget()
node_tree = self.id_data
if node_tree.bl_idname in {'SverchCustomTreeType', 'SverchGroupTreeType'}:
if node_tree.bl_idname in {'SverchCustomTreeType', }:
node_tree.nodes_dict.forget_node(self)
if hasattr(self, "has_3dview_props"): # todo remove
......@@ -382,7 +362,7 @@ class UpdateNodes:
self.n_id = ""
self.sv_copy(original)
if self.id_data.bl_idname in {'SverchCustomTreeType', 'SverchGroupTreeType'}:
if self.id_data.bl_idname in {'SverchCustomTreeType', }:
self.id_data.nodes_dict.load_node(self)
def update(self):
......@@ -417,10 +397,6 @@ class UpdateNodes:
debug("Partial update from node %s in %s", self.name, round(b - a, 4))
else:
process_from_node(self)
elif self.id_data.bl_idname == "SverchGroupTreeType":
monad = self.id_data
for instance in monad.instances:
instance.process_node(context)
elif self.id_data.bl_idname == "SvGroupTree":
self.id_data.update_nodes([self])
else:
......@@ -491,6 +467,10 @@ class NodeUtils:
if the text does not exist you get None
"""
if not identifier:
# this can happen if a json import goes through attributes arbitrarily.
self.info("no identifier passed to the get_bpy_data_from_name function.")
return None
try:
if isinstance(identifier, bpy.types.Object) and identifier.name in bpy_data_kind:
......@@ -501,13 +481,31 @@ class NodeUtils:
return bpy_data_kind.get(identifier)
elif identifier[3:] in bpy_data_kind:
return bpy_data_kind.get(identifier[3:])
return identifier
# something went wrong. the blend does not contain the objectname
self.info(f"{identifier} not found in {bpy_data_kind}, returning None instead")
if bpy_data_kind.bl_rna.identifier == 'BlendDataTexts':
# if we are in texts and this key is not found:
# - it's possible the named datablock incurred name collision
# - or it has not yet been created (usually json import, attribute order issue)
file_names = {t.name for t in bpy_data_kind}
self.info(f"The currently loaded blend file does contain the following text files {file_names}")
except Exception as err:
self.error(f"identifier '{identifier}' not found in {bpy_data_kind} - with error {err}")
return None
def safe_socket_remove(self, kind, key, failure_message=None):
with self.sv_throttle_tree_update():
sockets = getattr(self, kind)
if key in sockets:
sockets.remove(sockets[key])
else:
canned_msg = f"{self.name}.{kind} has no socket named {key} - did not remove"
self.debug(failure_message or canned_msg)
class SverchCustomTreeNode(UpdateNodes, NodeUtils):
"""Base class for all nodes"""
......@@ -524,7 +522,7 @@ class SverchCustomTreeNode(UpdateNodes, NodeUtils):
@classmethod
def poll(cls, ntree):
return ntree.bl_idname in ['SverchCustomTreeType', 'SverchGroupTreeType', 'SvGroupTree']
return ntree.bl_idname in ['SverchCustomTreeType', 'SvGroupTree']
@property
def absolute_location(self):
......
......@@ -98,6 +98,10 @@ class SvSetDataObjectNodeMK2(bpy.types.Node, SverchCustomTreeNode, SvAnimatableN
if Oo.is_linked:
Oo.sv_set(objs)
def draw_label(self):
if self.hide and self.formula:
return self.formula
return self.label or self.name
def register():
bpy.utils.register_class(SvSetDataObjectNodeMK2)
......
# ##### 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 #####
# the concrete monad classes plus common base
import ast
import pprint
import bpy
from bpy.types import Node
from bpy.props import StringProperty
from sverchok.node_tree import SverchCustomTreeNode
from sverchok.core import monad as monad_def
reverse_lookup = {'outputs': 'inputs', 'inputs': 'outputs'}
class SvSocketAquisition:
socket_map = {'outputs': 'to_socket', 'inputs': 'from_socket'}
node_kind: StringProperty()
@property
def get_outputs_info(self):
if self.node_kind == 'outputs':
return [{
'socket_name': s.name,
'socket_identifier': s.identifier,
'socket_prop_name': s.prop_name
} for s in self.outputs]
def update(self):
kind = self.node_kind
if not kind:
return
monad = self.id_data
if monad.bl_idname == "SverchCustomTreeType":
return
socket_list = getattr(self, kind)
_socket = self.socket_map.get(kind) # from_socket, to_socket
if len(socket_list) == 0:
print('sockets wiped, skipped update')
return
if socket_list[-1].is_linked:
# gather socket data
socket = socket_list[-1]
if kind == "outputs":
prop_name = monad.add_prop_from(socket)
cls = monad.update_cls()
new_name, new_type, prop_data = cls.input_template[-1]
else:
prop_name = ""
cls = monad.update_cls()
prop_data = {}
new_name, new_type = cls.output_template[-1]
new_socket = socket.replace_socket(new_type, new_name=new_name)
if prop_name:
new_socket.prop_name = prop_name
# update all monad nodes (front facing)
for instance in monad.instances:
sockets = getattr(instance, reverse_lookup[kind])
print(prop_data)
if prop_name:
print('prop_name!', prop_name)
with self.sv_throttle_tree_update():
new_socket = sockets.new(new_type, new_name)
if (not prop_data) and prop_name:
new_socket.prop_name = prop_name
else:
for name, value in prop_data.items():
if name == 'prop_name':
new_socket.prop_name = prop_name or ''
else:
setattr(new_socket, name, value)
# print('------')
# print(prop_data)
# pprint.pprint(self.get_outputs_info)
# add new dangling dummy
socket_list.new('SvDummySocket', 'connect me')
# stashing and repopulate are used for iojson
def stash(self):
socket_kinds = []
for s in getattr(self, self.node_kind):
if not s.bl_idname == 'SvDummySocket':
socket_kinds.append([s.name, s.bl_idname])
return socket_kinds
def repopulate(self, socket_kinds):
sockets = getattr(self, self.node_kind)
sockets.remove(sockets[0])
for idx, (s, stype) in enumerate(socket_kinds):
# print('add', s, stype, 'to', self.name)
sockets.new(stype, s)
def load_from_json(self, node_data: dict, import_version: float):
socket_kinds = node_data.get(self.node_kind)
self.repopulate(socket_kinds)
class SvGroupInputsNodeExp(Node, SvSocketAquisition, SverchCustomTreeNode):
bl_idname = 'SvGroupInputsNodeExp'
bl_label = 'Group Inputs Exp'
bl_icon = 'OUTLINER_OB_EMPTY'
def sv_init(self, context):
si = self.outputs.new
si('SvDummySocket', 'connect me')
self.node_kind = 'outputs'
self.use_custom_color = True
self.color = monad_def.MONAD_COLOR
class SvGroupOutputsNodeExp(Node, SvSocketAquisition, SverchCustomTreeNode):
bl_idname = 'SvGroupOutputsNodeExp'
bl_label = 'Group Outputs Exp'
bl_icon = 'OUTLINER_OB_EMPTY'
def sv_init(self, context):
si = self.inputs.new
si('SvDummySocket', 'connect me')
self.node_kind = 'inputs'
self.use_custom_color = True
self.color = monad_def.MONAD_COLOR
def call_init(self, context):
monad = self.monad
if self.monad:
self.input_template = monad.generate_inputs()
self.output_template = monad.generate_outputs()
class SvMonadGenericNode(Node, SverchCustomTreeNode, monad_def.SvGroupNodeExp):
bl_idname = 'SvMonadGenericNode'
bl_label = 'Group'
bl_icon = 'OUTLINER_OB_EMPTY'
data_storage: StringProperty()
cls_bl_idname: StringProperty(update=call_init)
@property
def input_template(self):
if not self.data_storage:
return []
data = ast.literal_eval(self.data_storage)
return data.get("input_template", {})
@input_template.setter
def input_template(self, value):
if self.data_storage:
data = ast.literal_eval(self.data_storage)
else:
data = {}
data["input_template"] = value
self.data_storage = str(data)
self.inputs.clear()
for socket_name, socket_bl_idname, _ in value:
self.inputs.new(socket_bl_idname, socket_name)
@property
def output_template(self):
if not self.data_storage:
return []
data = ast.literal_eval(self.data_storage)
return data.get("output_template", [])
@output_template.setter
def output_template(self, value):
if self.data_storage:
data = ast.literal_eval(self.data_storage)
else:
data = {}
data["output_template"] = value
self.data_storage = str(data)
self.outputs.clear()
for socket_name, socket_bl_idname in value:
self.outputs.new(socket_bl_idname, socket_name)
@property
def monad(self):
if not self.cls_bl_idname:
return None
for monad in bpy.data.node_groups:
if hasattr(monad, "cls_bl_idname"):
if monad.cls_bl_idname == self.cls_bl_idname:
return monad
return None
def sv_init(self, context):
self.use_custom_color = True
self.color = monad_def.MONAD_COLOR
class SvMonadInfoNode(bpy.types.Node, SverchCustomTreeNode):
''' Monad Info '''
bl_idname = 'SvMonadInfoNode'
bl_label = 'Monad Info'
bl_icon = 'OUTLINER_OB_EMPTY'
def sv_init(self, context):
self.outputs.new('SvStringsSocket', "Loop Idx")
self.outputs.new('SvStringsSocket', "Loop Total")
self.use_custom_color = True
self.color = monad_def.MONAD_COLOR
if self.id_data.bl_idname == "SverchCustomTreeType":
self.color = (0.9, 0, 0)
def process(self):
# outputs
monad = self.id_data
display_message = False
try:
idx = monad["current_index"]
total = monad["current_total"]
except Exception as err:
print("This error is being displayed by Monad Info: ", repr(err))
print("could not find monad[\"current_index\"] nor monad [\"current_total\"], setting to 0, 0 instead")
idx, total = 0 , 0
display_message = True
if display_message:
print('recovering from error, processing anyway')
for socket, data in zip(self.outputs, [idx, total]):
if socket.is_linked:
socket.sv_set([[data]])
classes = [
SvGroupInputsNodeExp,
SvGroupOutputsNodeExp,
SvMonadGenericNode,
SvMonadInfoNode
]
def register():
for cls in classes:
bpy.utils.register_class(cls)
def unregister():
for cls in classes:
bpy.utils.unregister_class(cls)
......@@ -225,7 +225,7 @@ class SvFormulaNodeMk5(bpy.types.Node, SverchCustomTreeNode):
"""
self.info('handling input wipe and relink')
self.debug('handling input wipe and relink')
nodes = self.id_data.nodes
node_tree = self.id_data
......
......@@ -269,10 +269,12 @@ class SvMeshEvalNode(bpy.types.Node, SverchCustomTreeNode, SvAnimatableNode):
sv_icon = 'SV_MESH_EXPRESSION'
def captured_updateNode(self, context):
if not self.updating_name_from_pointer:
if not self.updating_name_from_pointer and self.filename:
self.debug("triggered captured_updateNode, not good, not terrible.")
text_datablock = self.get_bpy_data_from_name(self.filename, bpy.data.texts)
if text_datablock:
self.font_pointer = text_datablock
if isinstance(text_datablock, bpy.types.Text):
self.file_pointer = text_datablock
self.adjust_sockets()
updateNode(self, context)
......@@ -327,11 +329,13 @@ class SvMeshEvalNode(bpy.types.Node, SverchCustomTreeNode, SvAnimatableNode):
self.outputs.new('SvStringsSocket', "FaceData")
def load_json(self):
internal_file = bpy.data.texts[self.filename]
f = io.StringIO(internal_file.as_string())
json_data = json.load(f)
self.validate_json(json_data)
return json_data
# internal_file = bpy.data.texts[self.filename]
internal_file = self.get_bpy_data_from_name(self.filename, bpy.data.texts)
if internal_file:
f = io.StringIO(internal_file.as_string())
json_data = json.load(f)
self.validate_json(json_data)
return json_data
def validate_json(self, json):
if not "vertices" in json:
......@@ -427,7 +431,8 @@ class SvMeshEvalNode(bpy.types.Node, SverchCustomTreeNode, SvAnimatableNode):
for key in self.inputs.keys():
if key not in variables:
self.debug("Input {} not in variables {}, remove it".format(key, str(variables)))
self.inputs.remove(self.inputs[key])
self.safe_socket_remove('inputs', key)
for v in variables:
if v not in self.inputs:
self.debug("Variable {} not in inputs {}, add it".format(v, str(self.inputs.keys())))
......@@ -439,7 +444,8 @@ class SvMeshEvalNode(bpy.types.Node, SverchCustomTreeNode, SvAnimatableNode):
continue
if key not in groups:
self.debug("Output {} not in groups {}, remove it".format(key, str(groups)))
self.outputs.remove(self.outputs[key])
self.safe_socket_remove('outputs', key)
for name in sorted(groups):
if name not in self.outputs:
self.debug("Group {} not in outputs {}, add it".format(name, str(self.outputs.keys())))
......
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать