def toggle(self): """ Toggle the selection state of the current face. """ self.current_face.select = not self.current_face.select debug("Set face #%s selection := %s", self.current_face.index, self.current_face.select)
def register(): global our_menu_classes debug("Registering sverchok-open3d") settings.register() icons.register() sockets.register() register_nodes() extra_nodes = importlib.import_module(".nodes", "sverchok_open3d") auto_gather_node_classes(extra_nodes) add_nodes_to_sv() menu.register() cats_menu = make_categories() # This would load every sverchok-open3d category straight in the Sv menu menu_category_provider = SvO3CategoryProvider("SVERCHOK_OPEN3D", cats_menu, DOCS_LINK, use_custom_menu=True, custom_menu='NODEVIEW_MT_Open3Dx') register_extra_category_provider(menu_category_provider) #if 'SVERCHOK_OPEN3D' in nodeitems_utils._node_categories: examples.register() # with make_categories() This would load every sverchok-open3d category straight in the Sv menu # our_menu_classes = make_extra_category_menus() show_welcome()
def binpack(nodes, max_bin_height, spacing=0): ''' Add nodes to the bins of given max bin height and spacing ''' if nodes: debug("There are %d nodes to bin pack" % (len(nodes))) for node in nodes: if node == None: debug("WARNING: a None node in the spawned nodes???") else: debug("WARNING: there are no nodes to bin pack!!!") return [] scale = 1.0 / get_dpi_factor() # dpi adjustment scale items = [NodeItem(node.dimensions.x * scale, node.dimensions.y * scale, node.bl_idname, node) for node in nodes] items = sorted(items, key=lambda item: item.height, reverse=True) bins = [] for item in items: # try to fit the next item into the first bin that is not yet full for n, bin in enumerate(bins): # check all the bins created so far if bin.height + len(bin.items) * spacing + item.height <= max_bin_height: # bin not full ? => add item debug("ADDING node <%s> to bin #%d" % (item.name, n)) bin.append(item) break # proceed to the next item else: # item didn't fit into any bin ? => add it to a new bin debug('ADDING node <%s> to new bin' % (item.name)) bin = Bin() bin.append(item) bins.append(bin) return bins
def assert_logs_no_errors(self): """ Assert that the code does not write any ERROR to the log. Usage: with self.assert_logs_no_errors(): info("this is just an information, not error") """ has_errors = False class Handler(logging.Handler): def emit(self, record): nonlocal has_errors if record.levelno >= logging.ERROR: has_errors = True handler = Handler() logging.getLogger().addHandler(handler) try: debug("=== \/ === [%s] Here should be no errors === \/ ===", self.__class__.__name__) yield handler self.assertFalse(has_errors, "There were some errors logged") finally: debug("=== /\ === [%s] There should be no errors === /\ ===", self.__class__.__name__) logging.getLogger().handlers.remove(handler)
def add_keymap(): # handle the keymap wm = bpy.context.window_manager kc = wm.keyconfigs.addon if not kc: debug('no keyconfig path found. that\'s ok') return km = kc.keymaps.new(name='Text', space_type='TEXT_EDITOR') keymaps = km.keymap_items if 'noderefresh_from_texteditor' in dir(bpy.ops.text): ''' SHORTCUT 1 Node Refresh: Ctrl + Return ''' ident_str = 'text.noderefresh_from_texteditor' if not (ident_str in keymaps): new_shortcut = keymaps.new(ident_str, 'RET', 'PRESS', ctrl=1, head=0) addon_keymaps.append((km, new_shortcut)) ''' SHORTCUT 2 Show svplugMenu Ctrl + I (no text selected) ''' new_shortcut = keymaps.new('wm.call_menu', 'I', 'PRESS', ctrl=1, head=0) new_shortcut.properties.name = 'TEXT_MT_svplug_menu' addon_keymaps.append((km, new_shortcut)) debug('added keyboard items to Text Editor.')
def _create_node_tree( name="TestingTree", must_not_exist=True, ) -> bpy.types.BlendDataNodeTrees: """ Create new Sverchok node tree in the scene. Description: hello Args: name (str, optional): Defaults to "TestingTree" must_not_exist (bool, optional): Defaults to True. If must_not_exist == True (default), then it is checked that the tree with such name did not exist before. If it exists, an exception is raised. If must_not_exist == False, then new tree will be created anyway, but it can be created with another name (standard Blender's renaming) Raises: _ExistingTreeError: Tree name already existed Returns: bpy.BlendDataNodeTrees: Created nodeTree """ if must_not_exist: if name in bpy.data.node_groups: raise _ExistingTreeError( f"Will not create tree `{name}': it already exists", ) debug(f"Creating tree: {name}") return bpy.data.node_groups.new(name=name, type="SverchCustomTreeType")
def process_node(self, context): ''' Doesn't work as intended, inherited functions can't be used for bpy.props update= ... Still this is called from updateNode ''' if self.id_data.bl_idname == "SverchCustomTreeType": if self.id_data.is_frozen(): return # self.id_data.has_changed = True if data_structure.DEBUG_MODE: a = time.perf_counter() process_from_node(self) b = time.perf_counter() 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) else: pass
def update_point(self, context): if hasattr(context, 'node') and hasattr(context.node, 'on_update'): context.node.on_update(context) else: debug( "Node is not defined in this context, so will not update the node." )
def generate_layout(fullpath, nodes_json): # it may be necessary to store monads as dicts instead of string/json # this will handle both scenarios if isinstance(nodes_json, str): nodes_json = json.loads(nodes_json) debug('==== loading monad ====') info(('#' * 12) + nodes_json['export_version']) # create all nodes and groups ''' update_lists = nodes_json['update_lists'] nodes_to_import = nodes_json['nodes'] if center is not None: center_nodes(nodes_to_import, center) groups_to_import = nodes_json.get('groups', {}) add_groups(groups_to_import) # this return is not used yet name_remap = add_nodes(ng, nodes_to_import, nodes, create_texts) # now connect them / prevent unnecessary updates ng.freeze(hard=True) make_links(update_lists, name_remap) # set frame parents ''' place_frames(ng, nodes_json, name_remap) # clean up old_nodes.scan_for_old(ng) ng.unfreeze(hard=True) ng.update() ng.update_tag()
def generate_layout(fullpath, nodes_json): # it may be necessary to store monads as dicts instead of string/json # this will handle both scenarios if isinstance(nodes_json, str): nodes_json = json.loads(nodes_json) debug('==== loading monad ====') info(('#' * 12) + nodes_json['export_version']) # create all nodes and groups ''' update_lists = nodes_json['update_lists'] nodes_to_import = nodes_json['nodes'] if center is not None: center_nodes(nodes_to_import, center) groups_to_import = nodes_json.get('groups', {}) add_groups(groups_to_import) # this return is not used yet name_remap = add_nodes(ng, nodes_to_import, nodes, create_texts) # now connect them / prevent unnecessary updates ng.freeze(hard=True) make_links(update_lists, name_remap) # set frame parents ''' place_frames(ng, nodes_json, name_remap) # clean up old_nodes.scan_for_old(ng) ng.unfreeze(hard=True) ng.update() ng.update_tag()
def SvGetSocket(socket, deepcopy=True): """gets socket data from socket, if deep copy is True a deep copy is make_dep_dict, to increase performance if the node doesn't mutate input set to False and increase performance substanstilly """ global socket_data_cache if socket.is_linked: other = socket.other s_id = other.socket_id s_ng = other.id_data.tree_id if s_ng not in socket_data_cache: raise LookupError if s_id in socket_data_cache[s_ng]: out = socket_data_cache[s_ng][s_id] if deepcopy: return sv_deep_copy(out) else: return out else: if data_structure.DEBUG_MODE: debug( f"cache miss: {socket.node.name} -> {socket.name} from: {other.node.name} -> {other.name}" ) raise SvNoDataError(socket, msg="not found in socket_data_cache") # not linked raise SvNoDataError(socket)
def make_add_operator(self): """ Create operator class which adds specific preset nodes to current node tree. Tooltip (docstring) for that operator is copied from metainfo/description field. """ global preset_add_operators if self.category is None: self.category = GENERAL if (self.category, self.name) not in preset_add_operators: preset_name = self.name if not self.description else self.description class SverchPresetAddOperator(bpy.types.Operator): bl_idname = "node.sv_preset_" + get_preset_idname_for_operator(self.name, self.category) bl_label = "Add {} preset ({} category)".format(preset_name, self.category) bl_options = {'REGISTER', 'UNDO'} cursor_x: bpy.props.IntProperty() cursor_y: bpy.props.IntProperty() @classmethod def poll(cls, context): try: return context.space_data.node_tree.bl_idname in {'SverchCustomTreeType', 'SverchGroupTreeType'} except: return False def execute(operator, context): # please not be confused: "operator" here references to # SverchPresetAddOperator instance, and "self" references to # SvPreset instance. ntree = context.space_data.node_tree id_tree = ntree.name ng = bpy.data.node_groups[id_tree] center = tuple(context.space_data.cursor_location) # Deselect everything, so as a result only imported nodes # will be selected bpy.ops.node.select_all(action='DESELECT') import_tree(ng, self.path, center = center) bpy.ops.transform.translate('INVOKE_DEFAULT') return {'FINISHED'} def invoke(self, context, event): self.cursor_x = event.mouse_region_x self.cursor_y = event.mouse_region_y return self.execute(context) SverchPresetAddOperator.__name__ = self.name SverchPresetAddOperator.__doc__ = self.meta.get("description", self.name) if self.standard: SverchPresetAddOperator.__doc__ += " [standard]" preset_add_operators[(self.category, self.name)] = SverchPresetAddOperator bpy.utils.register_class(SverchPresetAddOperator) debug("Registered: %s", "node.sv_preset_" + get_preset_idname_for_operator(self.name, self.category))
def execute(self, context): if not self.old_node_name: self.report({'ERROR'}, "Old node name is not provided") return {'CANCELLED'} if not self.new_bl_idname: self.report({'ERROR'}, "New node bl_idname is not provided") return {'CANCELLED'} tree = context.space_data.edit_tree old_node = tree.nodes[self.old_node_name] new_node = tree.nodes.new(self.new_bl_idname) # Copy UI properties ui_props = ['location', 'height', 'width', 'label', 'hide'] for prop_name in ui_props: setattr(new_node, prop_name, getattr(old_node, prop_name)) # Copy ID properties for prop_name, prop_value in old_node.items(): new_node[prop_name] = old_node[prop_name] # Copy incoming / outgoing links old_in_links = [link for link in tree.links if link.to_node == old_node] old_out_links = [link for link in tree.links if link.from_node == old_node] for old_link in old_in_links: new_target_socket_name = self.get_new_input_name(old_link.to_socket.name) if new_target_socket_name in new_node.inputs: new_target_socket = new_node.inputs[new_target_socket_name] new_link = tree.links.new(old_link.from_socket, new_target_socket) else: debug("New node %s has no input named %s, skipping", new_node.name, new_target_socket_name) tree.links.remove(old_link) for old_link in old_out_links: new_source_socket_name = self.get_new_output_name(old_link.from_socket.name) # We have to remove old link before creating new one # Blender would not allow two links pointing to the same target socket old_target_socket = old_link.to_socket tree.links.remove(old_link) if new_source_socket_name in new_node.outputs: new_source_socket = new_node.outputs[new_source_socket_name] new_link = tree.links.new(new_source_socket, old_target_socket) else: debug("New node %s has no output named %s, skipping", new_node.name, new_source_socket_name) if hasattr(new_node, "migrate_from"): # Allow new node to copy what generic code could not. new_node.migrate_from(old_node) msg = "Node `{}' ({}) has been replaced with new node `{}' ({})".format( old_node.name, old_node.bl_idname, new_node.name, new_node.bl_idname) info(msg) self.report({'INFO'}, msg) tree.nodes.remove(old_node) return {'FINISHED'}
def get_color(bl_id): """ Get color for bl_id """ if not colors_cache: debug("building color cache") rebuild_color_cache() return colors_cache.get(bl_id)
def create_node(node_type, tree_name=None): """ Create Sverchok node by it's bl_idname. """ if tree_name is None: tree_name = "TestingTree" debug("Creating node of type %s", node_type) return bpy.data.node_groups[tree_name].nodes.new(type=node_type)
def create_node(node_type, tree_name=None): """ Create Sverchok node by it's bl_idname. """ if tree_name is None: tree_name = "TestingTree" debug("Creating node of type %s", node_type) return bpy.data.node_groups[tree_name].nodes.new(type=node_type)
def get_color(bl_id): """ Get color for bl_id """ if not colors_cache: debug("building color cache") rebuild_color_cache() return colors_cache.get(bl_id)
def click(self): """ Jump to the face which is beyond the edge at which the turtle is looking currently. This turtle is a bit strange, because when it jumps it turns around, to look at the same edge it was looking, but from another side: +----+----+ +----+----+ | @> | | --> | | <@ | +----+----+ +----+----+ This changes the selection state of the face where the turtle stepped, if it is in "start_selecting()" mode, according to selection mask. This updates custom data layers of the face where the turtle stepped, if it is in "start_painting()" mode, according to painting masks. """ self.current_index += 1 self.current_face[self.index_layer] = self.current_index next_loop = self.current_loop.link_loop_radial_next self.current_loop = next_loop self.current_face = next_loop.face debug("Current face # := %s", self.current_face.index) if self.selection_mode == self.MASK: if not self.selection_mask: raise Exception("Selection mode is set to MASK, but mask is not specified") n = len(self.selection_mask) self.selection_cycle_index = (self.selection_cycle_index + 1) % n mode = self.selection_mask[self.selection_cycle_index] if mode not in [self.SELECT, self.UNSELECT, self.TOGGLE, 0, 1, False, True]: raise Exception("Unsupported flag in the selection mask") if mode == True or mode == 1: mode = self.SELECT elif mode == False or mode == 0: mode = self.UNSELECT else: mode = self.selection_mode if mode == self.SELECT: self.select() elif mode == self.UNSELECT: self.unselect() elif mode == self.TOGGLE: self.toggle() if self.is_painting: for painting_layer in self.painting_layer.values(): painting_mask = self.painting_mask.get(painting_layer.name) if not painting_mask: raise Exception("Painting layer is set, but painting mask is not") n = len(painting_mask) self.painting_index[painting_layer.name] = (self.painting_index[painting_layer.name] + 1) % n value = painting_mask[self.painting_index[painting_layer.name]] self.current_face[painting_layer] = value debug("Paint face #%s, layer `%s' with value `%s'", self.current_face.index, painting_layer.name, value)
def do_update_general(node_list, nodes, procesed_nodes=set()): """ General update function for node set """ global graphs timings = [] graph = [] gather = graph.append total_time = 0 done_nodes = set(procesed_nodes) # this is a no-op if no bgl being drawn. clear_exception_drawing_with_bgl(nodes) for node_name in node_list: if node_name in done_nodes: continue try: node = nodes[node_name] start = time.perf_counter() if hasattr(node, "process"): node.process() delta = time.perf_counter() - start total_time += delta if data_structure.DEBUG_MODE: debug("Processed %s in: %.4f", node_name, delta) timings.append(delta) gather({ "name": node_name, "bl_idname": node.bl_idname, "start": start, "duration": delta }) except Exception as err: ng = nodes.id_data update_error_nodes(ng, node_name, err) #traceback.print_tb(err.__traceback__) 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) return None graphs.append(graph) if data_structure.DEBUG_MODE: debug("Node set updated in: %.4f seconds", total_time) return timings
def start(self): debug("* Timer {0}: START".format(self.timer_id)) if self.timer_status in [TIMER_STATUS_STOPPED, TIMER_STATUS_EXPIRED]: debug("starting from zero") self.timer_time = 0 self.timer_start_frame = bpy.context.scene.frame_current self.timer_loop_count = 0 self.timer_status = TIMER_STATUS_STARTED self.last_timer_status = TIMER_STATUS_STARTED
def finish_task(cls): try: gc.enable() debug( f'Global update - {int((time() - cls._start_time) * 1000)}ms') cls._report_progress() finally: cls._event, cls._handler, cls._node_tree_area, cls._last_node, cls._start_time = [ None ] * 5
def apply_socket_props(socket, info): debug("applying socket props") for tracked_prop_name, tracked_prop_value in info.items(): try: setattr(socket, tracked_prop_name, tracked_prop_value) except Exception as err: error("Error while setting node socket: %s | %s", node.name, socket.index) error("the following failed | %s <- %s", tracked_prop_name, tracked_prop_value) exception(err)
def apply_socket_props(socket, info): debug("applying socket props") for tracked_prop_name, tracked_prop_value in info.items(): try: setattr(socket, tracked_prop_name, tracked_prop_value) except Exception as err: error("Error while setting node socket: %s | %s", node.name, socket.index) error("the following failed | %s <- %s", tracked_prop_name, tracked_prop_value) exception(err)
def save(self): if self._data is None: debug("Preset `%s': no data was loaded, nothing to save.", self.name) return data = json.dumps(self.data, sort_keys=True, indent=2).encode('utf8') with open(self.path, 'wb') as jsonfile: jsonfile.write(data) info("Saved preset `%s'", self.name)
def apply_custom_socket_props(node, node_ref): debug("applying node props for node: %s", node.bl_idname) socket_properties = node_ref.get('custom_socket_props') if socket_properties: for idx, info in socket_properties.items(): try: socket = node.inputs[int(idx)] apply_socket_props(socket, info) except Exception as err: error("socket index: %s, trying to pass: %s, num_sockets: %s", idx, info, len(node.inputs)) exception(err)
def get_node_tree(name=None): """ Return existing node tree, or raise an exception if there is no such. """ if name is None: name = "TestingTree" if name in bpy.data.node_groups: debug("Using existing tree: %s", name) return bpy.data.node_groups[name] else: raise Exception("There is no node tree named `{}'".format(name))
def get_node_tree(name=None): """ Return existing node tree, or raise an exception if there is no such. """ if name is None: name = "TestingTree" if name in bpy.data.node_groups: debug("Using existing tree: %s", name) return bpy.data.node_groups[name] else: raise Exception("There is no node tree named `{}'".format(name))
def apply_custom_socket_props(node, node_ref): debug("applying node props for node: %s", node.bl_idname) socket_properties = node_ref.get('custom_socket_props') if socket_properties: for idx, info in socket_properties.items(): try: socket = node.inputs[int(idx)] apply_socket_props(socket, info) except Exception as err: error("socket index: %s, trying to pass: %s, num_sockets: %s", idx, info, len(node.inputs)) exception(err)
def get_or_create_node_tree(name=None): """ Create new Sverchok node tree or reuse existing one. """ if name is None: name = "TestingTree" if name in bpy.data.node_groups: debug("Using existing tree: %s", name) return bpy.data.node_groups[name] else: return create_node_tree(name)
def handle_reload_event(nodes, imported_modules, old_nodes): node_list = make_node_list(nodes) reload_all(imported_modules, node_list, old_nodes) try: from sverchok.old_nodes import old_bl_idnames debug('Known old_bl_idnames after reload: %s', len(old_bl_idnames)) except Exception as err: exception(err) return node_list
def get_or_create_node_tree(name=None): """ Create new Sverchok node tree or reuse existing one. """ if name is None: name = "TestingTree" if name in bpy.data.node_groups: debug("Using existing tree: %s", name) return bpy.data.node_groups[name] else: return create_node_tree(name)
def compile_socket(link): try: link_data = (link.from_node.name, link.from_socket.index, link.to_node.name, link.to_socket.index) except Exception as err: if "'NodeSocketColor' object has no attribute 'index'" in repr(err): debug('adding node reroute using socketname instead if index') else: error(repr(err)) link_data = (link.from_node.name, link.from_socket.name, link.to_node.name, link.to_socket.name) return link_data
def compile_socket(link): try: link_data = (link.from_node.name, link.from_socket.index, link.to_node.name, link.to_socket.index) except Exception as err: if "'NodeSocketColor' object has no attribute 'index'" in repr(err): debug('adding node reroute using socketname instead if index') else: error(repr(err)) link_data = (link.from_node.name, link.from_socket.name, link.to_node.name, link.to_socket.name) return link_data
def perform_scripted_node_inject(node, node_ref): ''' Scripted Node will no longer create alternative versions of a file. If a scripted node wants to make a file called 'inverse.py' and the current .blend already contains such a file, then for simplicity the importer will not try to create 'inverse.001.py' and reference that. It will instead do nothing and assume the existing python file is functionally the same. If you have files that work differently but have the same name, stop. ''' texts = bpy.data.texts params = node_ref.get('params') if params: script_name = params.get('script_name') script_content = params.get('script_str') with node.sv_throttle_tree_update(): if script_name and not (script_name in texts): new_text = texts.new(script_name) new_text.from_string(script_content) elif script_name and (script_name in texts): # This was added to fix existing texts with the same name but no / different content. if texts[script_name].as_string() == script_content: debug( "SN skipping text named `%s' - their content are the same", script_name) else: info( "SN text named `%s' already found in current, but content differs", script_name) new_text = texts.new(script_name) new_text.from_string(script_content) script_name = new_text.name info('SN text named replaced with %s', script_name) node.script_name = script_name node.script_str = script_content if node.bl_idname == 'SvScriptNode': node.user_name = "templates" # best would be in the node. node.files_popup = "sv_lang_template.sn" # import to reset easy fix node.load() elif node.bl_idname == 'SvScriptNodeLite': node.load() # node.storage_set_data(node_ref) else: node.files_popup = node.avail_templates(None)[0][0] node.load()
def _draw_text_handler(tree_id, node_id, text: str, color=(1, 1, 1, 1), scale=1.0, align='RIGHT', text_coordinates=None): """Draw the text in a node tree editor nearby the given node""" editor = bpy.context.space_data if editor.type != 'NODE_EDITOR': return if editor.tree_type not in {"SverchCustomTreeType", 'SvGroupTree'}: return if not editor.edit_tree or editor.edit_tree.tree_id != tree_id: return # this is less efficient because it requires search of the node each redraw call if not text_coordinates: if not any(n for n in editor.edit_tree.nodes if n.node_id == node_id): debug( f'Some node looks like was removed without removing bgl drawing, text: {text}' ) return # find node location node = next(n for n in editor.edit_tree.nodes if n.node_id == node_id) (x, y), z = _get_text_location(node, align), 0 # put static coordinates if there are a lot of nodes with text to draw (does not react on the node movements) else: (x, y), z = text_coordinates, 0 # https://github.com/nortikin/sverchok/issues/4247 ui_scale = bpy.context.preferences.system.ui_scale x, y = x * ui_scale, y * ui_scale # todo add scale from the preferences text_height = int(15 * scale * ui_scale) line_height = int(18 * scale * ui_scale) font_id = 0 dpi = 72 blf.size(font_id, text_height, dpi) blf.color(font_id, *color) for line in text.split('\n'): blf.position(font_id, x, y, z) blf.draw(font_id, line) y -= line_height
def SvForgetSocket(socket): """deletes socket data from cache""" global socket_data_cache if data_structure.DEBUG_MODE: if not socket.is_output: warning( f"{socket.node.name} forgetting input socket: {socket.name}") s_id = socket.socket_id s_ng = socket.id_data.tree_id try: socket_data_cache[s_ng].pop(s_id, None) except KeyError: debug("it was never there")
def navigate_category(self, direction): ''' Navigate to PREV or NEXT category ''' debug("Navigate to PREV or NEXT category") categories = get_category_names() for i, category in enumerate(categories): if self.category == category: # prev or next category (cycle around) new_index = (i + 2 * direction - 1) % len(categories) new_category = categories[new_index] self.category = new_category break
def parse_profile(src): # Strip comments # (hope no one uses # in expressions) cleaned = "" for line in src.split("\n"): comment_idx = line.find('#') if comment_idx != -1: line = line[:comment_idx] cleaned = cleaned + " " + line profile = parse(parse_definition, cleaned) debug(profile) return profile
def add_spawned_node(context, name): if not _spawned_nodes: _spawned_nodes["main"] = [] debug("ADDING spawned node: %s" % name) tree = context.space_data.edit_tree try: node = tree.nodes.new(name) _spawned_nodes["main"].append(node) except: print("EXCEPTION: failed to spawn node with name: ", name)
def display_introspection_info(node, k, v): if not isinstance(v, (float, int, str)): debug('//') debug("%s -> property: %s: %s", node.name, k, type(v)) if k in node.bl_rna.properties: debug(type(node.bl_rna.properties[k])) elif k in node: # something like node['lp'] , ID Property directly on the node instance. debug(type(node[k])) else: error('%s is not bl_rna or IDproperty.. please report this', k) debug('\\\\')
def display_introspection_info(node, k, v): if not isinstance(v, (float, int, str)): debug('//') debug("%s -> property: %s: %s", node.name, k, type(v)) if k in node.bl_rna.properties: debug(type(node.bl_rna.properties[k])) elif k in node: # something like node['lp'] , ID Property directly on the node instance. debug(type(node[k])) else: error('%s is not bl_rna or IDproperty.. please report this', k) debug('\\\\')
def remove_node_tree(name=None): """ Remove existing Sverchok node tree. """ if name is None: name = "TestingTree" if name in bpy.data.node_groups: win = bpy.context.window scr = win.screen areas = [area for area in scr.areas if area.type == 'NODE_EDITOR'] if len(areas): space = areas[0].spaces[0] space.node_tree = None debug("Removing tree: %s", name) tree = bpy.data.node_groups[name] bpy.data.node_groups.remove(tree)
def create_node_tree(name=None, must_not_exist=True): """ Create new Sverchok node tree in the scene. If must_not_exist == True (default), then it is checked that the tree with such name did not exist before. If it exists, an exception is raised. If must_not_exist == False, then new tree will be created anyway, but it can be created with another name (standard Blender's renaming). """ if name is None: name = "TestingTree" if must_not_exist: if name in bpy.data.node_groups: raise Exception("Will not create tree `{}': it already exists".format(name)) debug("Creating tree: %s", name) return bpy.data.node_groups.new(name=name, type="SverchCustomTreeType")
def get_target_location(node_tree): """ Calculate average location of selected nodes in the tree, or all nodes if there are no nodes selected. """ selection = [node for node in node_tree.nodes if node.select] if not len(selection): debug("No selection, using all nodes") selection = node_tree.nodes[:] n = len(selection) if not n: return [0,0] locations = [node.location for node in selection] location_sum = [sum(x) for x in zip(*locations)] average_location = [x / float(n) for x in location_sum] return average_location
def perform_scripted_node_inject(node, node_ref): ''' Scripted Node will no longer create alternative versions of a file. If a scripted node wants to make a file called 'inverse.py' and the current .blend already contains such a file, then for simplicity the importer will not try to create 'inverse.001.py' and reference that. It will instead do nothing and assume the existing python file is functionally the same. If you have files that work differently but have the same name, stop. ''' texts = bpy.data.texts params = node_ref.get('params') if params: script_name = params.get('script_name') script_content = params.get('script_str') if script_name and not (script_name in texts): new_text = texts.new(script_name) new_text.from_string(script_content) elif script_name and (script_name in texts): # This was added to fix existing texts with the same name but no / different content. if texts[script_name].as_string() == script_content: debug("SN skipping text named `%s' - their content are the same", script_name) else: info("SN text named `%s' already found in current, but content differs", script_name) new_text = texts.new(script_name) new_text.from_string(script_content) script_name = new_text.name info('SN text named replaced with %s', script_name) node.script_name = script_name node.script_str = script_content if node.bl_idname == 'SvScriptNode': node.user_name = "templates" # best would be in the node. node.files_popup = "sv_lang_template.sn" # import to reset easy fix node.load() elif node.bl_idname == 'SvScriptNodeLite': node.load() # node.storage_set_data(node_ref) else: node.files_popup = node.avail_templates(None)[0][0] node.load()
def apply_core_props(node, node_ref): params = node_ref['params'] if 'cls_dict' in params: return for p in params: val = params[p] try: setattr(node, p, val) except Exception as e: # FIXME: this is ugly, need to find better approach error_message = repr(e) # for reasons error(error_message) msg = 'failed to assign value to the node' debug("`%s': %s = %s: %s", node.name, p, val, msg) if "val: expected sequence items of type boolean, not int" in error_message: debug("going to convert a list of ints to a list of bools and assign that instead") setattr(node, p, [bool(i) for i in val])
def make_links(update_lists, name_remap): print_update_lists(update_lists) failed_connections = [] for link in update_lists: try: ng.links.new(*resolve_socket(*link, name_dict=name_remap)) except Exception as err: exception(err) failed_connections.append(link) continue if failed_connections: error("failed total: %s", len(failed_connections)) error(failed_connections) else: debug('no failed connections! awesome.')
def intersect_with_plane(self, plane2): """ Return an intersection of this plane with another one. input: PlaneEquation output: LineEquation or None, in case two planes are parallel. """ if self.is_parallel(plane2): debug("{} is parallel to {}".format(self, plane2)) return None # We need an arbitrary point on this plane and two vectors. # Draw two lines in this plane and see for theirs intersection # with another plane. p0 = self.nearest_point_to_origin() v1, v2 = self.two_vectors() # it might be that p0 belongs to plane2; in that case we choose # another point in the same plane if plane2.check(p0): # Since v1 and v2 are orthogonal, it may not be that they are # both parallel to plane2. if not plane2.is_parallel(v1): p0 = p0 + v1 else: p0 = p0 + v2 line1 = LineEquation.from_direction_and_point(v1, p0) line2 = LineEquation.from_direction_and_point(v2, p0) # it might be that one of vectors we chose is parallel to plane2 # (since we are choosing them arbitrarily); but from the way # we are choosing v1 and v2, we know they are orthogonal. # So if wee just rotate them by pi/4, they will no longer be # parallel to plane2. if plane2.is_parallel(line1) or plane2.is_parallel(line2): v1_new = v1 + v2 v2_new = v1 - v2 info("{}, {} => {}, {}".format(v1, v2, v1_new, v2_new)) line1 = LineEquation.from_direction_and_point(v1_new, p0) line2 = LineEquation.from_direction_and_point(v2_new, p0) p1 = plane2.intersect_with_line(line1) p2 = plane2.intersect_with_line(line2) return LineEquation.from_two_points(p1, p2)
def perform_svtextin_node_object(node, node_ref): ''' as it's a beta service, old IO json may not be compatible - in this interest of neat code we assume it finds everything. ''' texts = bpy.data.texts params = node_ref.get('params') # original textin used 'current_text', textin+ uses 'text' current_text = params.get('current_text', params.get('text')) # it's not clear from the exporter code why textmode parameter isn't stored # in params.. for now this lets us look in both places. ugly but whatever. textmode = params.get('textmode') if not textmode: textmode = node_ref.get('textmode') node.textmode = textmode if not current_text: info("`%s' doesn't store a current_text in params", node.name) elif not current_text in texts: new_text = texts.new(current_text) text_line_entry = node_ref['text_lines'] if node.textmode == 'JSON': if isinstance(text_line_entry, str): debug('loading old text json content / backward compatibility mode') elif isinstance(text_line_entry, dict): text_line_entry = json.dumps(text_line_entry['stored_as_json']) new_text.from_string(text_line_entry) else: # reaches here if (current_text) and (current_text in texts) # can probably skip this.. # texts[current_text].from_string(node_ref['text_lines']) debug('%s seems to reuse a text block loaded by another node - skipping', node.name)
def get_file_obj_from_zip(fullpath): ''' fullpath must point to a zip file. usage: nodes_json = get_file_obj_from_zip(fullpath) print(nodes_json['export_version']) ''' with zipfile.ZipFile(fullpath, "r") as jfile: exported_name = "" for name in jfile.namelist(): if name.endswith('.json'): exported_name = name break if not exported_name: error('zip contains no files ending with .json') return debug(exported_name + ' <') fp = jfile.open(exported_name, 'r') m = fp.read().decode() return json.loads(m)
def do_update_general(node_list, nodes, procesed_nodes=set()): """ General update function for node set """ global graphs timings = [] graph = [] total_time = 0 done_nodes = set(procesed_nodes) for node_name in node_list: if node_name in done_nodes: continue try: node = nodes[node_name] start = time.perf_counter() if hasattr(node, "process"): node.process() delta = time.perf_counter() - start total_time += delta if data_structure.DEBUG_MODE: debug("Processed %s in: %.4f", node_name, delta) timings.append(delta) graph.append({"name" : node_name, "bl_idname": node.bl_idname, "start": start, "duration": delta}) except Exception as err: ng = nodes.id_data update_error_nodes(ng, node_name, err) #traceback.print_tb(err.__traceback__) exception("Node %s had exception: %s", node_name, err) return None graphs.append(graph) if data_structure.DEBUG_MODE: debug("Node set updated in: %.4f seconds", total_time) return timings
def process_node(self, context): ''' Doesn't work as intended, inherited functions can't be used for bpy.props update= ... Still this is called from updateNode ''' if self.id_data.bl_idname == "SverchCustomTreeType": if self.id_data.is_frozen(): return if data_structure.DEBUG_MODE: a = time.perf_counter() process_from_node(self) b = time.perf_counter() 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) else: pass
def sv_colors_definition(): addon_name = sverchok.__name__ addon = bpy.context.user_preferences.addons.get(addon_name) debug("got addon") if addon: prefs = addon.preferences sv_node_colors = { "Viz": prefs.color_viz, "Text": prefs.color_tex, "Scene": prefs.color_sce, "Layout": prefs.color_lay, "Generator": prefs.color_gen, } else: sv_node_colors = default_theme sv_node_cats = make_node_cats() sv_cats_node = {} for ca, no in sv_node_cats.items(): for n in no: try: sv_cats_node[n[0]] = sv_node_colors[ca] except: sv_cats_node[n[0]] = False return sv_cats_node
def print_update_lists(update_lists): debug('update lists:') for ulist in update_lists: debug(ulist)
def execute(self, context): for tree in bpy.data.node_groups: if not tree.bl_idname == 'SverchCustomTreeType': continue templist = [] for node in tree.nodes: idname = node.bl_idname if idname in {'ObjectsNodeMK2', 'SvObjectsNodeMK3'}: debug('scans for get option %s %s', node.label, node.name) if any((s.links for s in node.outputs)): templist.append([node.label, node.name, ""]) elif idname in {'SvNumberNode', 'IntegerNode', 'FloatNode', 'SvListInputNode', 'SvColorInputNode', 'SvBmeshViewerNodeMK2'}: if idname != 'SvBmeshViewerNodeMK2': if not node.outputs: debug("Node %s does not have outputs", node.name) continue if len(node.inputs) and node.inputs[0].is_linked: debug("Node %s: first input is linked", node.name) continue if (not node.outputs[0].is_linked) or (node.to3d != True): debug("Node %s: first output is not linked or to3d == False", node.name) continue elif (node.to3d != True): debug("Node %s: first output is not linked or to3d == False", node.name) continue if 'Integer' in idname: templist.append([node.label, node.name, 'int_']) elif 'SvBmeshViewerNodeMK2' in idname: templist.append([node.label, node.name, 'basemesh_name']) elif 'Float' in idname: templist.append([node.label, node.name, 'float_']) elif idname == 'SvColorInputNode': templist.append([node.label, node.name, 'color_data']) elif 'SvListInputNode' in idname: if node.mode == 'vector': templist.append([node.label, node.name, 'vector_list']) elif node.mode == 'int_list': templist.append([node.label, node.name, 'int_list']) elif node.mode == 'float_list': templist.append([node.label, node.name, 'float_list']) else: kind = node.selected_mode templist.append([node.label, node.name, kind + '_']) templist.sort() templ = [[t[1], t[2]] for t in templist] tree.Sv3DProps.clear() for name, prop in templ: debug('sverchok 3d panel appended with %s %s',name, prop) item = tree.Sv3DProps.add() item.node_name = name item.prop_name = prop return {'FINISHED'}