Source code for snnib.blender.utils.geo_nodes_utils

"""utilities to manipulate blenders geometry nodes

Exceptions

Classes

Functions
    - `add_todo_node()`
    - `copy_geonodes()`
    - `clear_node_group()`
    - `create_node_group()`
    - `delete_geonode_groups()`
    - `exists_node_group()`
    - `get_node_by_label()`
    - `set_node_curve()`

Other Objects
"""

#%%imports
import bpy

import logging
from typing import List, Tuple, Union

logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)

#%%definitions
[docs] def add_todo_node( node_group:bpy.types.GeometryNodeTree, location:Tuple[float,float]=(0.0,0.0), ) -> bpy.types.Node: """adds a frame denoting a TODO to the node tree Parameters - `node_group` - `bpy.types.GeometryNodeTree` - geo nodes node tree to attach the node to - `location` - `Tuple[float,float]`, optional - location as `(x,y)` of the node - the default is `(0.0,0.0)` Raises Returns - `n_todo` - `bpy.types.Node` - created node Dependencies - `bpy` - `logging` - `typing` """ n_todo = node_group.nodes.new(type="NodeFrame") n_todo.label = "TODO" n_todo.use_custom_color = True n_todo.label_size = 45 n_todo.color = (1.0,0.4,0.0) n_todo.location = location return n_todo
[docs] def copy_geonodes( src:bpy.types.Object, targ:bpy.types.Object, ) -> bpy.types.NodesModifier: """sets the geo nodes modifier of `targ` to the same as `src` - only applies to first found geo nodes modifier - no action if no geo nodes modifier found Parameters - `src` - `bpy.types.Object` - source to copy geo nodes modifier from - `targ` - `bpy.types.Object` - target to copy geo nodes modifier to Raises Returns - `targ_gn_mod` - `bpy.types.NodesModifier` - created geo nodes modifier Dependencies - `bpy` - `logging` - `typing` """ #get source modifier src_gn_mod = None for mod in src.modifiers: if mod.type == "NODES": src_gn_mod = mod break if src_gn_mod is None: logger.warning("no geonodes found... ignoring...") return {'FINISHED'} # Add a new Geometry Nodes modifier to the target targ_gn_mod = targ.modifiers.new(name=src_gn_mod.name, type='NODES') # Copy the node group from the source modifier targ_gn_mod.node_group = src_gn_mod.node_group return targ_gn_mod
[docs] def clear_node_group( group_name:str ): """clears the node group of `group_name` - used to avoid cluttering in blender file when developing new node group - allow to stay in node group and see changes when rerunning scripts Parameters - `group_name` - `str` - name of the targeted geo nodes group Raises Returns Dependencies - `bpy` - `logging` - `typing` """ for ng in bpy.data.node_groups: if group_name==ng.name: ng.nodes.clear() ng.links.clear() ng.interface.clear() return {'FINISHED'}
[docs] def create_node_group( group_name:str, dev:bool ) -> bpy.types.GeometryNodeTree: """returns existing node group if `dev`, otherwise always creates new one - helper creating node groups based on specifications - overrides existing nodes when `dev==True` (creates new group if nonexistent) - creates new group without overriding if `dev!=True` Parameters - `group_name` - `str` - name of the targeted geo nodes group - `dev` - `bool`, optional - flag denoting if script is ran in development mode Raises Returns - `node_group` - `bpy.types.GeometryNodeTree` - created geo nodes node group Dependencies - `bpy` - `logging` - `typing` """ #node creation if dev: clear_node_group(group_name) if not exists_node_group(group_name): #new node group if it does not exist yet node_group = bpy.data.node_groups.new(name=group_name, type='GeometryNodeTree') else: node_group = bpy.data.node_groups[group_name] else: #new node group regardless (will enumerate if existent) node_group = bpy.data.node_groups.new(name=group_name, type='GeometryNodeTree') return node_group
[docs] def delete_geonode_groups( group_name:str ): """deletes all geometry node node groups with name `group_name` - used before generating new groups to avoid cluttering blender Parameters - `group_name` - `str` - name of the targeted geo nodes group Raises Returns Dependencies - `bpy` - `logging` - `typing` """ #get all matching goroups to_delete = [ng for ng in bpy.data.node_groups if ng.name == group_name and ng.bl_idname == "GeometryNodeTree"] #remove found groups for ng in to_delete: bpy.data.node_groups.remove(ng) return {'FINISHED'}
[docs] def exists_node_group( group_name:str ): """returns flag whether node group of `group_name` already exists - used to avoid cluttering in blender file when developing new node group - allows creation of new group only if it is not existing already Parameters - `group_name` - `str` - name of the targeted geo nodes group Raises Returns Dependencies - `bpy` - `logging` - `typing` """ return any([group_name==ng.name for ng in bpy.data.node_groups])
[docs] def get_node_by_label( node_tree:bpy.types.GeometryNodeTree, label:str, ) -> bpy.types.Node: """returns first node in `node_tree` that matches `label` Parameters - `node_tree` - `bpy.types.GeometryNodeTree` - geometry nodes node tree to extract node from - `label` - `str` - label of the node to search for Raises Returns - `node` - `bpy.types.Node` - extracted node - `None` if no matching node was found Dependencies - `bpy` - `logging` - `typing` """ for node in node_tree.nodes: if node.label == label: return node return None
[docs] def set_node_curve( node:bpy.types.ShaderNodeRGBCurve, channel_index:int, pts:List[List[float]], handle_types:List[str]=None ): """overrides node curve (i.e. RGB Curves node) with new curve Parameters - `node` - `bpy.types.ShaderNodeRGBCurve` - node to update the curve of - `channel_index` - `int` - index of the channel to target - 0 > r - 1 > g - 2 > b - 3 > combined - `pts` - `List[List[float]]` - coordinates to place the control points at - `handle_types` - `List[str]`, optional - handle types to use for each point in `pts` - the default is `None` - uses default settings Raises Returns Dependencies - `bpy` - `logging` - `typing` """ curve = node.mapping.curves[channel_index] #keep endpoints only while len(curve.points) > 2: curve.points.remove(curve.points[1]) for (x, y) in pts: curve.points.new(x, y) #remove old first points curve.points.remove(curve.points[0]) #remove old last points # for p in curve.points: print(p.location) curve.points.remove(curve.points[-2]) #set handle types if handle_types is not None: for idx, ht in enumerate(handle_types): curve.points[idx].handle_type = ht #apply changes node.mapping.update() return {'FINISHED'}
#%%registration def register(): pass def unregister(): pass