Source code for snnib.blender.geo_nodes

"""custom geometry nodes node groups

- defines geometry nodes (geo nodes) node trees
- makes them available in the [blender](https://www.blender.org/)s geo nodes
- naming pattern: `Snnib<Name>`

Exceptions

Classes

Functions
    - `network_container()` -- geo nodes node tree for a network container
    - `neurite_bends()` -- adds random bends to neurites
    - `neurite_branches()` -- adds random branches to geometry
    - `neurite_to_mesh()` -- converts neurite curves to mesh
    - `neurite_twist()` -- adds twist to neurites
    - `neuron_neurites()` -- combined group for a single neuron including its outgoing neurites
    - `position_global()` -- returns position in global (world) coordinates
    - `remesh()` -- similar to remesh modifier
    - `scale_radial()` -- applies scaling in radial direction
    - `spiketrain()` -- generates color-sequence for spiketrain encoding from given `SNNIB` spiketrain texture
    
Other Objects

"""

#%%imports
import bpy
from bpy.types import Node, NodeSocket
from bpy.props import FloatProperty

import importlib
import logging

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

from . import (DEV, utils)

importlib.reload(utils)
  
#%%definitions

[docs] def network_container(): """creates a geometry nodes node group to style the network container - converts to wireframe Parameters Raises Returns Dependencies - `bpy` - `logging` """ #group attributes group_name = "SnnibNetworkContainer" #delete if already existent utils.geo_nodes_utils.delete_geonode_groups(group_name) #new node group node_group = bpy.data.node_groups.new(name=group_name, type='GeometryNodeTree') #define interface geo_in = node_group.interface.new_socket( name="Network Container", description="Container of the network", in_out='INPUT', socket_type='NodeSocketGeometry' ) geo_out = node_group.interface.new_socket( name="Network Container", description="Container of the network", in_out='OUTPUT', socket_type='NodeSocketGeometry' ) ##add nodes n_group_input_1 = node_group.nodes.new(type="NodeGroupInput") n_group_input_1.location = (000, 100) n_group_output_1 = node_group.nodes.new(type="NodeGroupOutput") n_group_output_1.location = (400, 0) n_mesh2curve = node_group.nodes.new(type="GeometryNodeMeshToCurve") n_mesh2curve.location = (200, 0) #add connections node_group.links.new(n_group_input_1.outputs["Network Container"], n_mesh2curve.inputs["Mesh"]) node_group.links.new(n_mesh2curve.outputs["Curve"], n_group_output_1.inputs[0]) return
[docs] def neurite_bends(): """applies random bends to some curve - controlled via noise texture Parameters Raises Returns Dependencies - `bpy` - `logging` """ #group attributes group_name = "SnnibNeuriteBends" #creation node_group = utils.geo_nodes_utils.create_node_group(group_name, dev=DEV) #define interface host_in = node_group.interface.new_socket( name="Curve", description="Curve to apply bends to", in_out='INPUT', socket_type='NodeSocketGeometry', ) twist_in = node_group.interface.new_socket( name="Strength", description="Strength of the applied bends", in_out='INPUT', socket_type='NodeSocketFloat', ) twist_in.default_value = 1.0 twist_in = node_group.interface.new_socket( name="Scale", description="Scale of the applied bends", in_out='INPUT', socket_type='NodeSocketFloat', ) twist_in.default_value = 1.0 curve_out = node_group.interface.new_socket( name="Curve", description="Input `Curve` with bends applied", in_out='OUTPUT', socket_type='NodeSocketGeometry' ) #add i/o nodes x0, y0 = 0, 0 n_group_input_1 = node_group.nodes.new(type="NodeGroupInput") n_group_input_1.location = (x0+0, y0+0) n_group_output_1 = node_group.nodes.new(type="NodeGroupOutput") n_group_output_1.location = (x0+2000, y0+0) #main nodes x0, y0 = 200, 0 n_noise_tex = node_group.nodes.new(type="ShaderNodeTexNoise") n_noise_tex.location = (x0+0, y0-400) n_noise_tex.noise_dimensions = '4D' n_noise_tex.inputs["Roughness"].default_value = 0.2 n_noise_tex.inputs["Distortion"].default_value = 1.0 n_sep_xyz = node_group.nodes.new(type="ShaderNodeSeparateXYZ") n_sep_xyz.location = (x0+200, y0-400) n_m_mult = node_group.nodes.new(type="ShaderNodeMath") n_m_mult.operation = 'MULTIPLY' n_m_mult.inputs[1].default_value = -1.0 n_m_mult.location = (x0+200, y0-200) n_m_mult.hide = True n_m_maprange1 = node_group.nodes.new(type="ShaderNodeMapRange") n_m_maprange1.location = (x0+400, y0-400) n_m_maprange1.hide = True n_m_maprange2 = node_group.nodes.new(type="ShaderNodeMapRange") n_m_maprange2.location = (x0+400, y0-500) n_m_maprange2.hide = True n_comb_xyz = node_group.nodes.new(type="ShaderNodeCombineXYZ") n_comb_xyz.inputs["Z"].default_value = 0.0 n_comb_xyz.location = (x0+600, y0-400) n_normal = node_group.nodes.new(type="GeometryNodeInputNormal") n_normal.location = (x0+600, y0-600) n_vm_project = node_group.nodes.new(type="ShaderNodeVectorMath") n_vm_project.operation = 'PROJECT' n_vm_project.location = (x0+800, y0-400) n_spline_param = node_group.nodes.new(type="GeometryNodeSplineParameter") n_spline_param.location = (x0+800, y0-200) n_rgb_curve = node_group.nodes.new(type="ShaderNodeRGBCurve") n_rgb_curve.location = (x0+1000, y0-0) points = [[0.0,0.0],[0.07,0.0],[0.24,0.90],[1.0,1.0]] handle_types = ['AUTO_CLAMPED','AUTO_CLAMPED','AUTO_CLAMPED','AUTO'] utils.geo_nodes_utils.set_node_curve(n_rgb_curve, 3, points, handle_types) n_rgb_curve.mapping.update() n_vm_mult = node_group.nodes.new(type="ShaderNodeVectorMath") n_vm_mult.operation = 'MULTIPLY' n_vm_mult.location = (x0+1400, y0-200) n_set_pos = node_group.nodes.new(type="GeometryNodeSetPosition") n_set_pos.location = (x0+1600, y0-100) node_group.links.new(n_group_input_1.outputs["Curve"], n_set_pos.inputs["Geometry"]) node_group.links.new(n_group_input_1.outputs["Strength"], n_m_mult.inputs[0]) node_group.links.new(n_group_input_1.outputs["Strength"], n_m_maprange1.inputs["To Max"]) node_group.links.new(n_group_input_1.outputs["Strength"], n_m_maprange2.inputs["To Max"]) node_group.links.new(n_m_mult.outputs["Value"], n_m_maprange1.inputs["To Min"]) node_group.links.new(n_m_mult.outputs["Value"], n_m_maprange2.inputs["To Min"]) node_group.links.new(n_group_input_1.outputs["Scale"], n_noise_tex.inputs["Scale"]) node_group.links.new(n_noise_tex.outputs["Color"], n_sep_xyz.inputs["Vector"]) node_group.links.new(n_sep_xyz.outputs["X"], n_m_maprange1.inputs["Value"]) node_group.links.new(n_sep_xyz.outputs["Y"], n_m_maprange2.inputs["Value"]) node_group.links.new(n_m_maprange1.outputs["Result"], n_comb_xyz.inputs["X"]) node_group.links.new(n_m_maprange2.outputs["Result"], n_comb_xyz.inputs["Y"]) node_group.links.new(n_comb_xyz.outputs["Vector"], n_vm_project.inputs[0]) node_group.links.new(n_normal.outputs["Normal"], n_vm_project.inputs[1]) node_group.links.new(n_spline_param.outputs["Factor"], n_rgb_curve.inputs["Color"]) node_group.links.new(n_rgb_curve.outputs["Color"], n_vm_mult.inputs[0]) node_group.links.new(n_vm_project.outputs["Vector"], n_vm_mult.inputs[1]) node_group.links.new(n_vm_mult.outputs["Vector"], n_set_pos.inputs["Offset"]) node_group.links.new(n_set_pos.outputs["Geometry"], n_group_output_1.inputs["Curve"]) return
[docs] def neurite_branches(): """creates a set of branches in random directions originating at `Host Mesh` - used to procedurally generate outgoing non-connections Parameters Raises Returns Dependencies - `bpy` - `logging` """ #group attributes group_name = "SnnibNeuriteBranches" #creation node_group = utils.geo_nodes_utils.create_node_group(group_name, dev=DEV) #define interface host_in = node_group.interface.new_socket( name="Host Mesh", description="Host mesh serving as origin to neurites", in_out='INPUT', socket_type='NodeSocketGeometry', ) density_in = node_group.interface.new_socket( name="Density", description="Density of the randomly generated neurites on the `Host Mesh`", in_out='INPUT', socket_type='NodeSocketFloat', ) density_in.min_value = 0.0 density_in.default_value = 1.0 length_min_in = node_group.interface.new_socket( name="Length.Min", description="Minimum length of any neurite", in_out='INPUT', socket_type='NodeSocketFloat', ) length_min_in.min_value = 0.0 length_min_in.default_value = 3.0 length_max_in = node_group.interface.new_socket( name="Length.Max", description="Maximum length of any neurite", in_out='INPUT', socket_type='NodeSocketFloat', ) length_max_in.min_value = 0.0 length_max_in.default_value = 10.0 seed_in = node_group.interface.new_socket( name="Seed", description="Random seed for the distribution", in_out='INPUT', socket_type='NodeSocketInt', ) seed_in.default_value = 0 resolution_in = node_group.interface.new_socket( name="Resolution", description="Resolution of the generated neurite in meters", in_out='INPUT', socket_type='NodeSocketFloat', ) resolution_in.default_value = 0.1 twist_in = node_group.interface.new_socket( name="Twist", description="Amount twist of the generated neurite", in_out='INPUT', socket_type='NodeSocketFloat', ) twist_in.default_value = 14 bend_strength_in = node_group.interface.new_socket( name="Strength", description="Strength of the random bends of the generated neurite", in_out='INPUT', socket_type='NodeSocketFloat', ) bend_strength_in.default_value = 1.0 bend_scale_in = node_group.interface.new_socket( name="Scale", description="Scale of the random bends of the generated neurite", in_out='INPUT', socket_type='NodeSocketFloat', ) bend_scale_in.default_value = 1.0 diameter_in = node_group.interface.new_socket( name="Diameter", description="Scaling factor of neurite diameter", in_out='INPUT', socket_type='NodeSocketFloat', ) diameter_in.default_value = 0.1 resolution_profile_in = node_group.interface.new_socket( name="Profile Resolution", description="Resolution of the profile curve used to convert axons to mesh", in_out='INPUT', socket_type='NodeSocketInt', ) resolution_profile_in.default_value = 16 neuron_out = node_group.interface.new_socket( name="Mesh", description="`Host Mesh` including neurite branches", in_out='OUTPUT', socket_type='NodeSocketGeometry' ) #add i/o nodes x0, y0 = 0, 0 n_group_input_1 = node_group.nodes.new(type="NodeGroupInput") n_group_input_1.location = (x0+0, y0+0) n_group_output_1 = node_group.nodes.new(type="NodeGroupOutput") n_group_output_1.location = (x0+1800, y0+0) #generator nodes x0, y0 = 200, 0 n_points_on_faces = node_group.nodes.new(type="GeometryNodeDistributePointsOnFaces") n_points_on_faces.location = (x0+000, y0+100) n_rand_float = node_group.nodes.new(type="FunctionNodeRandomValue") n_rand_float.data_type = 'FLOAT' n_rand_float.location = (x0+200, y0-100) n_curve_line = node_group.nodes.new(type="GeometryNodeCurvePrimitiveLine") n_curve_line.location = (x0+200, y0-0) n_curve_line.hide = True n_instance_on_points = node_group.nodes.new(type="GeometryNodeInstanceOnPoints") n_instance_on_points.location = (x0+400, y0-0) n_realize_instances = node_group.nodes.new(type="GeometryNodeRealizeInstances") n_realize_instances.inputs["Depth"].default_value = 2 n_realize_instances.location = (x0+600, y0-000) n_resample_curve = node_group.nodes.new(type="GeometryNodeResampleCurve") n_resample_curve.inputs["Mode"].default_value = 'Length' n_resample_curve.location = (x0+800, y0-0) n_snnib_neur_twist = node_group.nodes.new(type="GeometryNodeGroup") n_snnib_neur_twist.node_tree = bpy.data.node_groups["SnnibNeuriteTwist"] n_snnib_neur_twist.location = (x0+1000, y0) n_snnib_neur_bends = node_group.nodes.new(type="GeometryNodeGroup") n_snnib_neur_bends.node_tree = bpy.data.node_groups["SnnibNeuriteBends"] n_snnib_neur_bends.location = (x0+1200, y0) n_snnib_neur_to_mesh = node_group.nodes.new(type="GeometryNodeGroup") n_snnib_neur_to_mesh.node_tree = bpy.data.node_groups["SnnibNeuriteToMesh"] n_snnib_neur_to_mesh.location = (x0+1400, y0) #connect ##group inputs node_group.links.new(n_group_input_1.outputs["Host Mesh"], n_points_on_faces.inputs["Mesh"]) node_group.links.new(n_group_input_1.outputs["Density"], n_points_on_faces.inputs["Density"]) node_group.links.new(n_group_input_1.outputs["Seed"], n_points_on_faces.inputs["Seed"]) node_group.links.new(n_group_input_1.outputs["Length.Min"], n_rand_float.inputs["Min"]) node_group.links.new(n_group_input_1.outputs["Length.Max"], n_rand_float.inputs["Max"]) node_group.links.new(n_group_input_1.outputs["Seed"], n_rand_float.inputs["Seed"]) node_group.links.new(n_group_input_1.outputs["Resolution"], n_resample_curve.inputs["Length"]) node_group.links.new(n_group_input_1.outputs["Twist"], n_snnib_neur_twist.inputs["Twist"]) node_group.links.new(n_group_input_1.outputs["Strength"], n_snnib_neur_bends.inputs["Strength"]) node_group.links.new(n_group_input_1.outputs["Scale"], n_snnib_neur_bends.inputs["Scale"]) node_group.links.new(n_group_input_1.outputs["Diameter"], n_snnib_neur_to_mesh.inputs["Diameter"]) node_group.links.new(n_group_input_1.outputs["Profile Resolution"], n_snnib_neur_to_mesh.inputs["Profile Resolution"]) ##main nodes node_group.links.new(n_points_on_faces.outputs["Points"], n_instance_on_points.inputs["Points"]) node_group.links.new(n_points_on_faces.outputs["Rotation"], n_instance_on_points.inputs["Rotation"]) node_group.links.new(n_curve_line.outputs["Curve"], n_instance_on_points.inputs["Instance"]) node_group.links.new(n_rand_float.outputs["Value"], n_instance_on_points.inputs["Scale"]) node_group.links.new(n_instance_on_points.outputs["Instances"], n_realize_instances.inputs["Geometry"]) node_group.links.new(n_realize_instances.outputs["Geometry"], n_resample_curve.inputs["Curve"]) node_group.links.new(n_resample_curve.outputs["Curve"], n_snnib_neur_twist.inputs["Curve"]) node_group.links.new(n_snnib_neur_twist.outputs["Curve"], n_snnib_neur_bends.inputs["Curve"]) node_group.links.new(n_snnib_neur_bends.outputs["Curve"], n_snnib_neur_to_mesh.inputs["Curve"]) node_group.links.new(n_snnib_neur_to_mesh.outputs["Mesh"], n_group_output_1.inputs["Mesh"]) return
[docs] def neurite_to_mesh(): """converts some input neurite (curve) to a mesh - applies thickness to neurite Parameters Raises Returns Dependencies - `bpy` - `logging` """ #group attributes group_name = "SnnibNeuriteToMesh" #creation node_group = utils.geo_nodes_utils.create_node_group(group_name, dev=DEV) #define interface curve_in = node_group.interface.new_socket( name="Curve", description="Curve representing the neuron", in_out='INPUT', socket_type='NodeSocketGeometry', ) diameter_in = node_group.interface.new_socket( name="Diameter", description="Scaling of the diameter that the input `curve` shall have", in_out='INPUT', socket_type='NodeSocketFloat', ) diameter_in.default_value = 0.1 resolution_profile_in = node_group.interface.new_socket( name="Profile Resolution", description="Resolution of the profile curve used to generate mesh", in_out='INPUT', socket_type='NodeSocketInt', ) resolution_profile_in.default_value = 16 mesh_out = node_group.interface.new_socket( name="Mesh", description="`Curve` converted to a mesh", in_out='OUTPUT', socket_type='NodeSocketGeometry' ) #add i/o nodes x0, y0 = 0, 0 n_group_input_1 = node_group.nodes.new(type="NodeGroupInput") n_group_input_1.location = (x0+0, y0+0) n_group_output_1 = node_group.nodes.new(type="NodeGroupOutput") n_group_output_1.location = (x0+1400, y0+0) #generator nodes x0, y0 = 200, 0 # utils.geo_nodes_utils.add_todo_node(node_group, (200,0)) n_spline_param = node_group.nodes.new(type="GeometryNodeSplineParameter") n_spline_param.location = (x0, y0-200) n_m_sub = node_group.nodes.new(type="ShaderNodeMath") n_m_sub.operation = 'SUBTRACT' n_m_sub.inputs[0].default_value = 1.0 n_m_sub.location = (x0+200, y0-200) n_rgb_curve = node_group.nodes.new(type="ShaderNodeRGBCurve") n_rgb_curve.location = (x0+400, y0-200) points = [[0.0,0.15],[0.02,0.85],[0.05,0.20],[1.0,1.0]] handle_types = ['AUTO','AUTO_CLAMPED','AUTO','AUTO'] utils.geo_nodes_utils.set_node_curve(n_rgb_curve, 3, points, handle_types) n_rgb_curve.mapping.update() n_m_mult = node_group.nodes.new(type="ShaderNodeMath") n_m_mult.operation = 'MULTIPLY' n_m_mult.location = (x0+800, y0-200) n_curve_circ = node_group.nodes.new(type="GeometryNodeCurvePrimitiveCircle") n_curve_circ.location = (x0+800, y0-100) n_curve_circ.hide = True n_curve_to_mesh = node_group.nodes.new(type="GeometryNodeCurveToMesh") n_curve_to_mesh.inputs["Fill Caps"].default_value = True n_curve_to_mesh.location = (x0+1000, y0-0) node_group.links.new(n_group_input_1.outputs["Curve"], n_curve_to_mesh.inputs["Curve"]) node_group.links.new(n_group_input_1.outputs["Diameter"], n_m_mult.inputs[0]) node_group.links.new(n_group_input_1.outputs["Profile Resolution"], n_curve_circ.inputs["Resolution"]) node_group.links.new(n_spline_param.outputs["Factor"], n_m_sub.inputs[1]) node_group.links.new(n_m_sub.outputs["Value"], n_rgb_curve.inputs["Color"]) node_group.links.new(n_rgb_curve.outputs["Color"], n_m_mult.inputs[1]) node_group.links.new(n_m_mult.outputs["Value"], n_curve_to_mesh.inputs["Scale"]) node_group.links.new(n_curve_circ.outputs["Curve"], n_curve_to_mesh.inputs["Profile Curve"]) node_group.links.new(n_curve_to_mesh.outputs["Mesh"], n_group_output_1.inputs["Mesh"]) return
[docs] def neurite_twist(): """applies twist to some curve Parameters Raises Returns Dependencies - `bpy` - `logging` """ #group attributes group_name = "SnnibNeuriteTwist" #creation node_group = utils.geo_nodes_utils.create_node_group(group_name, dev=DEV) #define interface host_in = node_group.interface.new_socket( name="Curve", description="Curve to be twisted", in_out='INPUT', socket_type='NodeSocketGeometry', ) twist_in = node_group.interface.new_socket( name="Twist", description="Amount twist of the generated neurite", in_out='INPUT', socket_type='NodeSocketFloat', ) twist_in.default_value = 14 curve_out = node_group.interface.new_socket( name="Curve", description="Input `Curve` with twist applied", in_out='OUTPUT', socket_type='NodeSocketGeometry' ) #add i/o nodes x0, y0 = 0, 0 n_group_input_1 = node_group.nodes.new(type="NodeGroupInput") n_group_input_1.location = (x0+0, y0+0) n_group_output_1 = node_group.nodes.new(type="NodeGroupOutput") n_group_output_1.location = (x0+1200, y0+0) #main nodes x0, y0 = 200, 0 n_spline_param = node_group.nodes.new(type="GeometryNodeSplineParameter") n_spline_param.location = (x0+0, y0-200) n_rgb_curve = node_group.nodes.new(type="ShaderNodeRGBCurve") n_rgb_curve.location = (x0+200, y0-200) points = [[0.0,0.45],[0.33,0.75],[1.0,1.0]] utils.geo_nodes_utils.set_node_curve(n_rgb_curve, 3, points) n_rgb_curve.mapping.update() n_m_mult = node_group.nodes.new(type="ShaderNodeMath") n_m_mult.operation = 'MULTIPLY' n_m_mult.location = (x0+600, y0-100) n_curve_tilt = node_group.nodes.new(type="GeometryNodeSetCurveTilt") n_curve_tilt.location = (x0+800, y0-0) node_group.links.new(n_group_input_1.outputs["Curve"], n_curve_tilt.inputs["Curve"]) node_group.links.new(n_group_input_1.outputs["Twist"], n_m_mult.inputs[0]) node_group.links.new(n_spline_param.outputs["Factor"], n_rgb_curve.inputs["Color"]) node_group.links.new(n_rgb_curve.outputs["Color"], n_m_mult.inputs[1]) node_group.links.new(n_m_mult.outputs["Value"], n_curve_tilt.inputs["Tilt"]) node_group.links.new(n_curve_tilt.outputs["Curve"], n_group_output_1.inputs["Curve"]) return
[docs] def neuron_neurites(): """creates template geonodes node group for defining neurons, dendrites, and axons - this group is used to make created networks customizable in an intuitive, procedual manner - contains all basic tweakable parameters to customize the look of neurons in the `SNNIB` network Parameters Raises Returns Dependencies - `bpy` - `logging` """ #group attributes group_name = "SnnibNeuronNeurites" #creation node_group = utils.geo_nodes_utils.create_node_group(group_name, dev=DEV) #define interface neuron_in = node_group.interface.new_socket( name="Neuron Object", description="Neuron", in_out='INPUT', socket_type='NodeSocketGeometry' ) spiketrain_in = node_group.interface.new_socket( name="Spiketrain", description="1D texture map representing the spiketrain", in_out='INPUT', socket_type='NodeSocketImage' ) axon_in = node_group.interface.new_socket( name="Axon Curve", description="Curve representing the neurons axon", in_out='INPUT', socket_type='NodeSocketObject' ) seed_in = node_group.interface.new_socket( name="Seed", description="Random seed to use for dendrite distribution", in_out='INPUT', socket_type='NodeSocketInt' ) neuron_out = node_group.interface.new_socket( name="Neuron", description="Neuron inluding axon", in_out='OUTPUT', socket_type='NodeSocketGeometry' ) #add i/o nodes x0, y0 = 0, 0 n_group_input_1 = node_group.nodes.new(type="NodeGroupInput") n_group_input_1.location = (x0+0, y0+0) n_group_output_1 = node_group.nodes.new(type="NodeGroupOutput") n_group_output_1.location = (x0+4200, y0+0) #------------------------------------------------------------- #control values x0, y0 = -200, -200 frame_c = node_group.nodes.new(type="NodeFrame") frame_c.label = "Controls" frame_c.location = (0,0) n_n_simsteps = node_group.nodes.new(type="ShaderNodeValue") n_n_simsteps.label = "Number of Simulation Steps" n_n_simsteps.location = (x0+0, y0) n_n_simsteps.outputs[0].default_value = 120 n_n_simsteps.parent = frame_c frame_cn = node_group.nodes.new(type="NodeFrame") frame_cn.label = "Neuron" frame_cn.location = (0,0) frame_cn.parent = frame_c n_neuron_pulsation_slowdown = node_group.nodes.new(type="ShaderNodeValue") n_neuron_pulsation_slowdown.label = "Neuron.Pulsation.Slowdown" n_neuron_pulsation_slowdown.location = (x0+0, y0-100) n_neuron_pulsation_slowdown.outputs[0].default_value = 75 n_neuron_pulsation_slowdown.parent = frame_cn n_neuron_pulsation_scale = node_group.nodes.new(type="ShaderNodeValue") n_neuron_pulsation_scale.label = "Neuron.Pulsation.Scale" n_neuron_pulsation_scale.location = (x0+0, y0-200) n_neuron_pulsation_scale.outputs[0].default_value = 0.5 n_neuron_pulsation_scale.parent = frame_cn frame_cst = node_group.nodes.new(type="NodeFrame") frame_cst.label = "Spike Train" frame_cst.location = (0,0) frame_cst.parent = frame_c n_spiketrain_offset = node_group.nodes.new(type="ShaderNodeValue") n_spiketrain_offset.label = "SpikeTrain.Offset" n_spiketrain_offset.location = (x0+0, y0-400) n_spiketrain_offset.outputs[0].default_value = 0 n_spiketrain_offset.parent = frame_cst n_spiketrain_stretch = node_group.nodes.new(type="ShaderNodeValue") n_spiketrain_stretch.label = "SpikeTrain.Stretch" n_spiketrain_stretch.location = (x0+0, y0-500) n_spiketrain_stretch.outputs[0].default_value = 0.2 n_spiketrain_stretch.parent = frame_cst frame_ca = node_group.nodes.new(type="NodeFrame") frame_ca.label = "Neurites" frame_ca.location = (0,0) frame_ca.parent = frame_c n_dendrite_diameter_scale = node_group.nodes.new(type="ShaderNodeValue") n_dendrite_diameter_scale.label = "Dendrite.Diameter.Scale" n_dendrite_diameter_scale.location = (x0+0, y0-700) n_dendrite_diameter_scale.outputs[0].default_value = 0.1 n_dendrite_diameter_scale.parent = frame_ca n_dendrite_resolution = node_group.nodes.new(type="ShaderNodeValue") n_dendrite_resolution.label = "Dendrite.Resolution" n_dendrite_resolution.location = (x0+0, y0-800) n_dendrite_resolution.outputs[0].default_value = 0.1 n_dendrite_resolution.parent = frame_ca n_axon_diameter_scale = node_group.nodes.new(type="ShaderNodeValue") n_axon_diameter_scale.label = "Axon.Diameter.Scale" n_axon_diameter_scale.location = (x0+0, y0-900) n_axon_diameter_scale.outputs[0].default_value = 0.2 n_axon_diameter_scale.parent = frame_ca n_m_mult_neuron_scale = node_group.nodes.new(type="ShaderNodeMath") n_m_mult_neuron_scale.location = (x0+200, y0-900) n_m_mult_neuron_scale.operation = 'MULTIPLY' n_m_mult_neuron_scale.parent = frame_ca n_axon_resolution = node_group.nodes.new(type="ShaderNodeValue") n_axon_resolution.label = "Axon.Resolution" n_axon_resolution.location = (x0+0, y0-1000) n_axon_resolution.outputs[0].default_value = 0.1 n_axon_resolution.parent = frame_ca n_neuron_scale = node_group.nodes.new(type="ShaderNodeValue") n_neuron_scale.label = "Neuron.Scale" n_neuron_scale.location = (x0+0, y0-1200) n_neuron_scale.outputs[0].default_value = 1.0 n_neuron_scale.parent = frame_c node_group.links.new(n_axon_diameter_scale.outputs["Value"], n_m_mult_neuron_scale.inputs[0]) node_group.links.new(n_neuron_scale.outputs["Value"], n_m_mult_neuron_scale.inputs[1]) #------------------------------------------------------------- #neuron body x0 = 300 y0 = -0 frame_nb = node_group.nodes.new(type="NodeFrame") frame_nb.label = "Neuron Body" frame_nb.location = (0,0) n_scene_time = node_group.nodes.new(type="GeometryNodeInputSceneTime") n_scene_time.location = (x0+000, y0-100) n_scene_time.parent = frame_nb n_snnib_pos_glob = node_group.nodes.new(type="GeometryNodeGroup") n_snnib_pos_glob.node_tree = bpy.data.node_groups["SnnibPositionGlobal"] n_snnib_pos_glob.location = (x0+200, y0) n_snnib_pos_glob.parent = frame_nb n_m_div = node_group.nodes.new(type="ShaderNodeMath") n_m_div.operation = "DIVIDE" n_m_div.location = (x0+200, y0-100) n_m_div.parent = frame_nb n_noise_tex = node_group.nodes.new(type="ShaderNodeTexNoise") n_noise_tex.location = (x0+400, y0-0) n_noise_tex.noise_dimensions = '4D' n_noise_tex.inputs["Scale"].default_value = 2.0 n_noise_tex.parent = frame_nb n_m_mult = node_group.nodes.new(type="ShaderNodeMath") n_m_mult.operation = "MULTIPLY" n_m_mult.location = (x0+600, y0-0) n_m_mult.parent = frame_nb n_snnib_scale_rad = node_group.nodes.new(type="GeometryNodeGroup") n_snnib_scale_rad.node_tree = bpy.data.node_groups["SnnibScaleRadial"] n_snnib_scale_rad.location = (x0+800, y0-0) n_snnib_scale_rad.parent = frame_nb n_trans_geo1 = node_group.nodes.new(type="GeometryNodeTransform") n_trans_geo1.location = (x0+1000, y0-0) n_trans_geo1.parent = frame_nb node_group.links.new(n_group_input_1.outputs["Neuron Object"], n_snnib_scale_rad.inputs["Geometry"]) node_group.links.new(n_scene_time.outputs["Frame"], n_m_div.inputs[0]) node_group.links.new(n_neuron_pulsation_slowdown.outputs["Value"], n_m_div.inputs[1]) node_group.links.new(n_neuron_pulsation_scale.outputs["Value"], n_m_mult.inputs[1]) node_group.links.new(n_m_div.outputs["Value"], n_noise_tex.inputs["W"]) node_group.links.new(n_snnib_pos_glob.outputs["Global Position"], n_noise_tex.inputs["Vector"]) node_group.links.new(n_noise_tex.outputs["Factor"], n_m_mult.inputs[0]) node_group.links.new(n_m_mult.outputs["Value"], n_snnib_scale_rad.inputs["Scale"]) node_group.links.new(n_neuron_scale.outputs["Value"], n_trans_geo1.inputs["Scale"]) node_group.links.new(n_snnib_scale_rad.outputs["Geometry"], n_trans_geo1.inputs["Geometry"]) #------------------------------------------------------------- #dendrites x0, y0 = 300, -600 frame_d = node_group.nodes.new(type="NodeFrame") frame_d.label = "Dendrites" frame_d.location = (0,0) n_snnib_neur_branches1 = node_group.nodes.new(type="GeometryNodeGroup") n_snnib_neur_branches1.node_tree = bpy.data.node_groups["SnnibNeuriteBranches"] n_snnib_neur_branches1.location = (x0, y0) n_snnib_neur_branches1.parent = frame_d n_trans_geo2 = node_group.nodes.new(type="GeometryNodeTransform") n_trans_geo2.location = (x0+200, y0-0) n_trans_geo2.parent = frame_d n_join_geo1 = node_group.nodes.new(type="GeometryNodeJoinGeometry") n_join_geo1.location = (x0+400, y0) n_join_geo1.parent = frame_d ##setting up repeat zone n_repeat_in = node_group.nodes.new(type="GeometryNodeRepeatInput") n_repeat_in.location = (x0+600, y0+0) n_repeat_in.parent = frame_d n_repeat_out = node_group.nodes.new(type="GeometryNodeRepeatOutput") n_repeat_out.location = (x0+1400, y0+0) n_repeat_out.repeat_items.new("FLOAT", "Density") n_repeat_out.repeat_items.new("FLOAT", "Length.Min") n_repeat_out.repeat_items.new("FLOAT", "Length.Max") n_repeat_out.repeat_items.new("FLOAT", "Diameter") n_repeat_out.parent = frame_d n_repeat_in.pair_with_output(n_repeat_out) #create pair n_repeat_in.inputs["Density"].default_value = 3.0 n_repeat_in.inputs["Length.Min"].default_value = 0.1 n_repeat_in.inputs["Length.Max"].default_value = 3.0 n_repeat_in.inputs["Diameter"].default_value = 0.03 n_snnib_neur_branches2 = node_group.nodes.new(type="GeometryNodeGroup") n_snnib_neur_branches2.node_tree = bpy.data.node_groups["SnnibNeuriteBranches"] n_snnib_neur_branches2.location = (x0+800, y0) n_snnib_neur_branches2.parent = frame_d n_join_geo2 = node_group.nodes.new(type="GeometryNodeJoinGeometry") n_join_geo2.location = (x0+1000, y0) ###updates frame_ud = node_group.nodes.new(type="NodeFrame") frame_ud.label = "Updates" frame_ud.location = (0,0) frame_ud.parent = frame_d n_m_mult1 = node_group.nodes.new(type="ShaderNodeMath") n_m_mult1.operation = "MULTIPLY" n_m_mult1.inputs[1].default_value = 0.5 n_m_mult1.location = (x0+800, y0-400) n_m_mult1.parent = frame_ud n_m_mult1.hide = True n_m_mult2 = node_group.nodes.new(type="ShaderNodeMath") n_m_mult2.operation = "MULTIPLY" n_m_mult2.inputs[1].default_value = 0.8 n_m_mult2.location = (x0+800, y0-450) n_m_mult2.parent = frame_ud n_m_mult2.hide = True n_m_mult3 = node_group.nodes.new(type="ShaderNodeMath") n_m_mult3.operation = "MULTIPLY" n_m_mult3.inputs[1].default_value = 0.8 n_m_mult3.location = (x0+800, y0-500) n_m_mult3.parent = frame_ud n_m_mult3.hide = True node_group.links.new(n_snnib_neur_branches1.outputs["Mesh"], n_trans_geo2.inputs["Geometry"]) node_group.links.new(n_trans_geo2.outputs["Geometry"], n_join_geo1.inputs["Geometry"]) node_group.links.new(n_neuron_scale.outputs["Value"], n_trans_geo2.inputs["Scale"]) node_group.links.new(n_join_geo1.outputs["Geometry"], n_repeat_in.inputs["Geometry"]) node_group.links.new(n_repeat_in.outputs["Geometry"], n_snnib_neur_branches2.inputs["Host Mesh"]) node_group.links.new(n_snnib_neur_branches2.outputs["Mesh"], n_join_geo2.inputs["Geometry"]) node_group.links.new(n_join_geo2.outputs["Geometry"], n_repeat_out.inputs["Geometry"]) node_group.links.new(n_group_input_1.outputs["Neuron Object"], n_snnib_neur_branches1.inputs["Host Mesh"]) node_group.links.new(n_group_input_1.outputs["Seed"], n_snnib_neur_branches1.inputs["Seed"]) node_group.links.new(n_dendrite_resolution.outputs["Value"], n_snnib_neur_branches1.inputs["Resolution"]) node_group.links.new(n_repeat_in.outputs["Geometry"], n_join_geo2.inputs["Geometry"]) node_group.links.new(n_dendrite_diameter_scale.outputs["Value"], n_snnib_neur_branches1.inputs["Diameter"]) node_group.links.new(n_repeat_in.outputs["Length.Min"], n_m_mult1.inputs[0]) node_group.links.new(n_repeat_in.outputs["Length.Max"], n_m_mult2.inputs[0]) node_group.links.new(n_repeat_in.outputs["Diameter"], n_m_mult3.inputs[0]) node_group.links.new(n_repeat_in.outputs["Density"], n_snnib_neur_branches2.inputs["Density"]) node_group.links.new(n_repeat_in.outputs["Length.Min"], n_snnib_neur_branches2.inputs["Length.Min"]) node_group.links.new(n_repeat_in.outputs["Length.Max"], n_snnib_neur_branches2.inputs["Length.Max"]) node_group.links.new(n_repeat_in.outputs["Diameter"], n_snnib_neur_branches2.inputs["Diameter"]) node_group.links.new(n_m_mult2.outputs["Value"], n_repeat_out.inputs["Length.Min"]) node_group.links.new(n_m_mult3.outputs["Value"], n_repeat_out.inputs["Length.Max"]) node_group.links.new(n_m_mult1.outputs["Value"], n_repeat_out.inputs["Diameter"]) #------------------------------------------------------------- #axon x0, y0 = 300, -1300 frame_ax = node_group.nodes.new(type="NodeFrame") frame_ax.label = "Axon" frame_ax.location = (0,0) n_obj_info = node_group.nodes.new(type="GeometryNodeObjectInfo") n_obj_info.location = (x0+0, y0-0) n_obj_info.transform_space = 'RELATIVE' n_obj_info.parent = frame_ax n_res_curve = node_group.nodes.new(type="GeometryNodeResampleCurve") n_res_curve.location = (x0+200, y0-0) n_res_curve.inputs["Mode"].default_value = 'Length' n_res_curve.parent = frame_ax n_spline_param = node_group.nodes.new(type="GeometryNodeSplineParameter") n_spline_param.location = (x0+0, y0-300) n_spline_param.parent = frame_ax n_rgb_curve = node_group.nodes.new(type="ShaderNodeRGBCurve") n_rgb_curve.location = (x0+200, y0-300) points = [[0.0,0.0],[0.25,0.0],[0.50,1.00],[0.75,0.0],[1.0,0.0]] handle_types = ['AUTO_CLAMPED','AUTO_CLAMPED','AUTO_CLAMPED','AUTO_CLAMPED'] utils.geo_nodes_utils.set_node_curve(n_rgb_curve, 3, points, handle_types) n_rgb_curve.mapping.update() n_rgb_curve.parent = frame_ax n_m_mult = node_group.nodes.new(type="ShaderNodeMath") n_m_mult.operation = "MULTIPLY" n_m_mult.inputs[1].default_value = 3.0 n_m_mult.location = (x0+500, y0-300) n_m_mult.parent = frame_ax n_snnib_neur_twist = node_group.nodes.new(type="GeometryNodeGroup") n_snnib_neur_twist.node_tree = bpy.data.node_groups["SnnibNeuriteTwist"] n_snnib_neur_twist.location = (x0+400, y0-0) n_snnib_neur_twist.inputs["Twist"].default_value = 8.0 n_snnib_neur_twist.parent = frame_ax n_snnib_neur_bends = node_group.nodes.new(type="GeometryNodeGroup") n_snnib_neur_bends.node_tree = bpy.data.node_groups["SnnibNeuriteBends"] n_snnib_neur_bends.location = (x0+600, y0-0) n_snnib_neur_bends.inputs["Scale"].default_value = 0.4 n_snnib_neur_bends.parent = frame_ax n_snnib_neur_to_mesh = node_group.nodes.new(type="GeometryNodeGroup") n_snnib_neur_to_mesh.node_tree = bpy.data.node_groups["SnnibNeuriteToMesh"] n_snnib_neur_to_mesh.location = (x0+800, y0-0) n_snnib_neur_to_mesh.parent = frame_ax n_comb_xyz = node_group.nodes.new(type="ShaderNodeCombineXYZ") n_comb_xyz.location = (x0+800, y0-200) n_comb_xyz.inputs[0].default_value = 1.0 n_comb_xyz.inputs[1].default_value = 1.0 n_comb_xyz.inputs[2].default_value = 1.0 n_comb_xyz.parent = frame_ax n_trans_geo3 = node_group.nodes.new(type="GeometryNodeTransform") n_trans_geo3.location = (x0+1000, y0-0) n_trans_geo3.parent = frame_ax node_group.links.new(n_group_input_1.outputs["Axon Curve"], n_obj_info.inputs["Object"]) node_group.links.new(n_axon_resolution.outputs["Value"], n_res_curve.inputs["Length"]) node_group.links.new(n_m_mult_neuron_scale.outputs["Value"], n_snnib_neur_to_mesh.inputs["Diameter"]) node_group.links.new(n_obj_info.outputs["Geometry"], n_res_curve.inputs["Curve"]) node_group.links.new(n_res_curve.outputs["Curve"], n_snnib_neur_twist.inputs["Curve"]) node_group.links.new(n_snnib_neur_twist.outputs["Curve"], n_snnib_neur_bends.inputs["Curve"]) node_group.links.new(n_spline_param.outputs["Factor"], n_rgb_curve.inputs["Factor"]) node_group.links.new(n_rgb_curve.outputs["Color"], n_m_mult.inputs[0]) node_group.links.new(n_m_mult.outputs["Value"], n_snnib_neur_bends.inputs["Strength"]) node_group.links.new(n_snnib_neur_bends.outputs["Curve"], n_snnib_neur_to_mesh.inputs["Curve"]) node_group.links.new(n_snnib_neur_to_mesh.outputs["Mesh"], n_trans_geo3.inputs["Geometry"]) node_group.links.new(n_comb_xyz.outputs["Vector"], n_trans_geo3.inputs["Scale"]) node_group.links.new(n_trans_geo3.outputs["Geometry"], n_join_geo1.inputs["Geometry"]) #------------------------------------------------------------- #combining x0, y0 = 2200, 0 n_join_geo = node_group.nodes.new(type="GeometryNodeJoinGeometry") n_join_geo.location = (x0, y0) n_snnib_remesh = node_group.nodes.new(type="GeometryNodeGroup") n_snnib_remesh.node_tree = bpy.data.node_groups["SnnibRemesh"] n_snnib_remesh.location = (x0+200, y0-0) n_snnib_remesh.mute = True n_snnib_spiketrain = node_group.nodes.new(type="GeometryNodeGroup") n_snnib_spiketrain.node_tree = bpy.data.node_groups["SnnibSpiketrain"] n_snnib_spiketrain.location = (x0+400, y0+200) n_store_attr = node_group.nodes.new(type="GeometryNodeStoreNamedAttribute") n_store_attr.data_type = 'FLOAT_COLOR' n_store_attr.inputs["Name"].default_value = "Spiketrain.Texture" n_store_attr.location = (x0+600, y0) n_set_mat = node_group.nodes.new(type="GeometryNodeSetMaterial") n_set_mat.location = (x0+800, y0) n_set_mat.inputs["Material"].default_value = bpy.data.materials.get("SnnibSpikingNeuron") # n_set_mat.parent = utils.geo_nodes_utils.add_todo_node(node_group) n_shade_smooth = node_group.nodes.new(type="GeometryNodeSetShadeSmooth") n_shade_smooth.location = (x0+1000, y0) node_group.links.new(n_trans_geo1.outputs["Geometry"], n_join_geo.inputs["Geometry"]) node_group.links.new(n_repeat_out.outputs["Geometry"], n_join_geo.inputs["Geometry"]) node_group.links.new(n_join_geo.outputs["Geometry"], n_snnib_remesh.inputs["Geometry"]) node_group.links.new(n_snnib_remesh.outputs["Geometry"], n_snnib_spiketrain.inputs["Geometry"]) node_group.links.new(n_group_input_1.outputs["Spiketrain"], n_snnib_spiketrain.inputs["Spiketrain"]) node_group.links.new(n_spiketrain_offset.outputs["Value"], n_snnib_spiketrain.inputs["Offset"]) node_group.links.new(n_spiketrain_stretch.outputs["Value"], n_snnib_spiketrain.inputs["Stretch"]) node_group.links.new(n_n_simsteps.outputs["Value"], n_snnib_spiketrain.inputs["Number of Frames"]) node_group.links.new(n_snnib_remesh.outputs["Geometry"], n_store_attr.inputs["Geometry"]) node_group.links.new(n_snnib_spiketrain.outputs["Spiketrain"], n_store_attr.inputs["Value"]) node_group.links.new(n_store_attr.outputs["Geometry"], n_set_mat.inputs["Geometry"]) node_group.links.new(n_set_mat.outputs["Geometry"], n_shade_smooth.inputs["Mesh"]) node_group.links.new(n_shade_smooth.outputs["Mesh"], n_group_output_1.inputs["Neuron"]) return
[docs] def position_global(): """creates a geometry nodes node group that returns position in global (world) coordinates - useful for defining one GeoNodes modifier and applying it to different objects scattered throughout the scene - i.e., small random variations in each neuron Parameters Raises Returns Dependencies - `bpy` - `logging` """ #group attributes group_name = "SnnibPositionGlobal" #creation node_group = utils.geo_nodes_utils.create_node_group(group_name, dev=DEV) #define interface global_position_out = node_group.interface.new_socket( name="Global Position", description="objects position in the global coordinate system", in_out='OUTPUT', socket_type='NodeSocketVector' ) #add nodes n_self_object_1 = node_group.nodes.new(type="GeometryNodeSelfObject") n_self_object_1.location = (0, 0) n_obj_info_1 = node_group.nodes.new(type="GeometryNodeObjectInfo") n_obj_info_1.location = (200, 0) n_position_1 = node_group.nodes.new(type="GeometryNodeInputPosition") n_position_1.location = (200, -300) n_vector_math_add_1 = node_group.nodes.new(type="ShaderNodeVectorMath") n_vector_math_add_1.operation = 'ADD' n_vector_math_add_1.location = (400, 0) n_group_output_1 = node_group.nodes.new(type="NodeGroupOutput") n_group_output_1.location = (600, 0) #add connections node_group.links.new(n_self_object_1.outputs["Self Object"], n_obj_info_1.inputs[0]) node_group.links.new(n_obj_info_1.outputs["Location"], n_vector_math_add_1.inputs[0]) node_group.links.new(n_position_1.outputs["Position"], n_vector_math_add_1.inputs[1]) node_group.links.new(n_vector_math_add_1.outputs[0], n_group_output_1.inputs[0]) return
[docs] def remesh(): """creates a geometry nodes node group similar to remesh modifier - achieved by voxelizing the input Parameters Raises Returns Dependencies - `bpy` - `logging` """ #group attributes group_name = "SnnibRemesh" #creation node_group = utils.geo_nodes_utils.create_node_group(group_name, dev=DEV) #define interface geo_in = node_group.interface.new_socket( name="Geometry", description="Geometry to be remeshed", in_out='INPUT', socket_type='NodeSocketGeometry' ) voxel_size_in = node_group.interface.new_socket( name="Voxel Size", description="Size to use for the voxels", in_out='INPUT', socket_type='NodeSocketFloat' ) voxel_size_in.default_value = 0.02 threshold_in = node_group.interface.new_socket( name="Threshold", description="Threshold to use to convert voxelized geometry to mesh", in_out='INPUT', socket_type='NodeSocketFloat' ) threshold_in.default_value = 0.02 geo_out = node_group.interface.new_socket( name="Geometry", description="geometry after being scaled radially", in_out='OUTPUT', socket_type='NodeSocketGeometry' ) #add nodes x0, y0 = 0, 0 n_group_input_1 = node_group.nodes.new(type="NodeGroupInput") n_group_input_1.location = (x0, y0) n_group_output_1 = node_group.nodes.new(type="NodeGroupOutput") n_group_output_1.location = (800, 0) n_mesh_to_grid = node_group.nodes.new(type="GeometryNodeMeshToSDFGrid") n_mesh_to_grid.location = (x0+200, y0-0) n_voxelize = node_group.nodes.new(type="GeometryNodeGridVoxelize") n_voxelize.location = (x0+400, y0-0) n_grid_to_mesh = node_group.nodes.new(type="GeometryNodeGridToMesh") n_grid_to_mesh.location = (x0+600, y0-0) node_group.links.new(n_group_input_1.outputs["Geometry"], n_mesh_to_grid.inputs["Mesh"]) node_group.links.new(n_group_input_1.outputs["Voxel Size"], n_mesh_to_grid.inputs["Voxel Size"]) node_group.links.new(n_group_input_1.outputs["Threshold"], n_grid_to_mesh.inputs["Threshold"]) node_group.links.new(n_mesh_to_grid.outputs["SDF Grid"], n_voxelize.inputs["Grid"]) node_group.links.new(n_voxelize.outputs["Grid"], n_grid_to_mesh.inputs["Grid"]) node_group.links.new(n_grid_to_mesh.outputs["Mesh"], n_group_output_1.inputs["Geometry"]) return
[docs] def scale_radial(): """creates a geometry nodes node group that scales input geometry radially Parameters Raises Returns Dependencies - `bpy` - `logging` """ #group attributes group_name = "SnnibScaleRadial" #creation node_group = utils.geo_nodes_utils.create_node_group(group_name, dev=DEV) #define interface geo_in = node_group.interface.new_socket( name="Geometry", description="geometry to be scaled in radial direction", in_out='INPUT', socket_type='NodeSocketGeometry' ) scale_in = node_group.interface.new_socket( name="Scale", description="vector (or texture) to use for scaling", in_out='INPUT', socket_type='NodeSocketVector' ) geo_out = node_group.interface.new_socket( name="Geometry", description="geometry after being scaled radially", in_out='OUTPUT', socket_type='NodeSocketGeometry' ) #add nodes n_group_input_1 = node_group.nodes.new(type="NodeGroupInput") n_group_input_1.location = (000, 100) n_position_1 = node_group.nodes.new(type="GeometryNodeInputPosition") n_position_1.location = (200, 0) n_vector_math_norm_1 = node_group.nodes.new(type="ShaderNodeVectorMath") n_vector_math_norm_1.operation = 'NORMALIZE' n_vector_math_norm_1.location = (400, 0) n_vector_math_mult_1 = node_group.nodes.new(type="ShaderNodeVectorMath") n_vector_math_mult_1.operation = 'MULTIPLY' n_vector_math_mult_1.location = (600, 0) n_set_position_1 = node_group.nodes.new(type="GeometryNodeSetPosition") n_set_position_1.location = (800, 0) n_group_output_1 = node_group.nodes.new(type="NodeGroupOutput") n_group_output_1.location = (1000, 0) #add connections node_group.links.new(n_group_input_1.outputs["Geometry"], n_set_position_1.inputs[0]) node_group.links.new(n_group_input_1.outputs["Scale"], n_vector_math_mult_1.inputs[1]) node_group.links.new(n_position_1.outputs["Position"], n_vector_math_norm_1.inputs[0]) node_group.links.new(n_vector_math_norm_1.outputs["Vector"], n_vector_math_mult_1.inputs[0]) node_group.links.new(n_vector_math_mult_1.outputs["Vector"], n_set_position_1.inputs[3]) node_group.links.new(n_set_position_1.outputs["Geometry"], n_group_output_1.inputs[0]) return
[docs] def spiketrain(): """creates a geometry nodes node group to generate colors for spiketrain - result stored as attribute to be used downstream for texturing Parameters Raises Returns Dependencies - `bpy` - `logging` """ #group attributes group_name = "SnnibSpiketrain" #creation node_group = utils.geo_nodes_utils.create_node_group(group_name, dev=DEV) #define interface geo_in = node_group.interface.new_socket( name="Geometry", description="Geometry to be remeshed", in_out='INPUT', socket_type='NodeSocketGeometry' ) spiketrain_in = node_group.interface.new_socket( name="Spiketrain", description="1d image representing the spiketrain (pixels with values of 0 or 1)", in_out='INPUT', socket_type='NodeSocketImage' ) offset_in = node_group.interface.new_socket( name="Offset", description="Offset of the spiketrain", in_out='INPUT', socket_type='NodeSocketInt' ) stretch_in = node_group.interface.new_socket( name="Stretch", description="Stretch of the spiketrain", in_out='INPUT', socket_type='NodeSocketFloat' ) nframes_in = node_group.interface.new_socket( name="Number of Frames", description="Number of frames in the entire animation", in_out='INPUT', socket_type='NodeSocketInt' ) color_out = node_group.interface.new_socket( name="Spiketrain", description="Color representing the spiketrain", in_out='OUTPUT', socket_type='NodeSocketColor' ) #add nodes x0, y0 = 0, 0 n_group_input_1 = node_group.nodes.new(type="NodeGroupInput") n_group_input_1.location = (x0, y0) n_group_output_1 = node_group.nodes.new(type="NodeGroupOutput") n_group_output_1.location = (3000, 0) #------------------------------------------------------------- #r_norm x0, y0 = 200, 0 frame_rn = node_group.nodes.new(type="NodeFrame") frame_rn.label = "r_norm \in [0,1]" frame_rn.location = (0,0) x0, y0 = 200, 0 frame_r = node_group.nodes.new(type="NodeFrame") frame_r.label = "r \in \mathbb{R}" frame_r.location = (0,0) frame_r.parent = frame_rn n_pos = node_group.nodes.new(type="GeometryNodeInputPosition") n_pos.location = (x0, y0) n_pos.parent = frame_r n_obj_info = node_group.nodes.new(type="GeometryNodeObjectInfo") n_obj_info.location = (x0, y0-100) n_obj_info.parent = frame_r n_vm_dist1 = node_group.nodes.new(type="ShaderNodeVectorMath") n_vm_dist1.location = (x0+200, y0) n_vm_dist1.operation = "DISTANCE" n_vm_dist1.parent = frame_r node_group.links.new(n_pos.outputs["Position"], n_vm_dist1.inputs[0]) node_group.links.new(n_obj_info.outputs["Location"], n_vm_dist1.inputs[1]) x0, y0 = 200, -400 frame_ro = node_group.nodes.new(type="NodeFrame") frame_ro.label = "Object radius \in \mathbb{R}" frame_ro.location = (0,0) frame_ro.parent = frame_rn n_bound_box = node_group.nodes.new(type="GeometryNodeBoundBox") n_bound_box.location = (x0, y0) n_bound_box.parent = frame_ro n_vm_dist2 = node_group.nodes.new(type="ShaderNodeVectorMath") n_vm_dist2.location = (x0+200, y0) n_vm_dist2.operation = "DISTANCE" n_vm_dist2.parent = frame_ro n_m_mult1 = node_group.nodes.new(type="ShaderNodeMath") n_m_mult1.location = (x0+400, y0) n_m_mult1.operation = "MULTIPLY" n_m_mult1.inputs[1].default_value = 0.5 n_m_mult1.parent = frame_ro node_group.links.new(n_group_input_1.outputs["Geometry"], n_bound_box.inputs["Geometry"]) node_group.links.new(n_bound_box.outputs["Min"], n_vm_dist2.inputs[0]) node_group.links.new(n_bound_box.outputs["Max"], n_vm_dist2.inputs[1]) node_group.links.new(n_vm_dist2.outputs["Value"], n_m_mult1.inputs[0]) n_m_div1 = node_group.nodes.new(type="ShaderNodeMath") n_m_div1.location = (x0+600, y0+200) n_m_div1.operation = "DIVIDE" n_m_div1.parent = frame_rn node_group.links.new(n_vm_dist1.outputs["Value"], n_m_div1.inputs[0]) node_group.links.new(n_m_mult1.outputs["Value"], n_m_div1.inputs[1]) #------------------------------------------------------------- #time x0, y0 = 200, -700 frame_t = node_group.nodes.new(type="NodeFrame") frame_t.label = "Time \in [-1,0]" frame_t.location = (0,0) n_scene_time = node_group.nodes.new(type="GeometryNodeInputSceneTime") n_scene_time.location = (x0, y0) n_scene_time.parent = frame_t n_m_div2 = node_group.nodes.new(type="ShaderNodeMath") n_m_div2.location = (x0+200, y0) n_m_div2.operation = "DIVIDE" n_m_div2.parent = frame_t n_m_mult2 = node_group.nodes.new(type="ShaderNodeMath") n_m_mult2.location = (x0+400, y0) n_m_mult2.operation = "MULTIPLY" n_m_mult2.inputs[1].default_value = -1.0 n_m_mult2.parent = frame_t node_group.links.new(n_scene_time.outputs["Frame"], n_m_div2.inputs[0]) node_group.links.new(n_group_input_1.outputs["Number of Frames"], n_m_div2.inputs[1]) node_group.links.new(n_m_div2.outputs["Value"], n_m_mult2.inputs[0]) #------------------------------------------------------------- #offset x0, y0 = 900, -400 frame_os = node_group.nodes.new(type="NodeFrame") frame_os.label = "Offset" frame_os.location = (0,0) n_m_div3 = node_group.nodes.new(type="ShaderNodeMath") n_m_div3.location = (x0+200, y0) n_m_div3.operation = "DIVIDE" n_m_div3.parent = frame_os n_m_sub = node_group.nodes.new(type="ShaderNodeMath") n_m_sub.location = (x0+400, y0) n_m_sub.operation = "SUBTRACT" n_m_sub.parent = frame_os node_group.links.new(n_group_input_1.outputs["Offset"], n_m_div3.inputs[0]) node_group.links.new(n_group_input_1.outputs["Number of Frames"], n_m_div3.inputs[1]) node_group.links.new(n_m_div1.outputs["Value"], n_m_sub.inputs[0]) node_group.links.new(n_m_div3.outputs["Value"], n_m_sub.inputs[1]) #------------------------------------------------------------- #stretch x0, y0 = 1600, -400 frame_st = node_group.nodes.new(type="NodeFrame") frame_st.label = "Stretch" frame_st.location = (0,0) n_m_mult3 = node_group.nodes.new(type="ShaderNodeMath") n_m_mult3.location = (x0, y0) n_m_mult3.operation = "MULTIPLY" n_m_mult3.parent = frame_st node_group.links.new(n_m_sub.outputs["Value"], n_m_mult3.inputs[0]) node_group.links.new(n_group_input_1.outputs["Stretch"], n_m_mult3.inputs[1]) #------------------------------------------------------------- #index x0, y0 = 2000, -400 frame_id = node_group.nodes.new(type="NodeFrame") frame_id.label = "Index \in [0,1]" frame_id.location = (0,0) n_m_add = node_group.nodes.new(type="ShaderNodeMath") n_m_add.location = (x0, y0) n_m_add.operation = "ADD" n_m_add.parent = frame_id n_m_sub = node_group.nodes.new(type="ShaderNodeMath") n_m_sub.location = (x0+200, y0) n_m_sub.operation = "SUBTRACT" n_m_sub.inputs[0].default_value = 1.0 n_m_sub.parent = frame_id n_comb_xyz = node_group.nodes.new(type="ShaderNodeCombineXYZ") n_comb_xyz.location = (x0+400, y0) n_comb_xyz.inputs["Y"].default_value = 1.0 n_comb_xyz.inputs["Z"].default_value = 1.0 n_comb_xyz.parent = frame_id node_group.links.new(n_m_mult3.outputs["Value"], n_m_add.inputs[0]) node_group.links.new(n_m_mult2.outputs["Value"], n_m_add.inputs[1]) node_group.links.new(n_m_add.outputs["Value"], n_m_sub.inputs[1]) node_group.links.new(n_m_sub.outputs["Value"], n_comb_xyz.inputs["X"]) n_img_tex = node_group.nodes.new(type="GeometryNodeImageTexture") n_img_tex.location = (x0+600, y0) node_group.links.new(n_group_input_1.outputs["Spiketrain"], n_img_tex.inputs["Image"]) node_group.links.new(n_comb_xyz.outputs["Vector"], n_img_tex.inputs["Vector"]) node_group.links.new(n_img_tex.outputs["Color"], n_group_output_1.inputs["Spiketrain"]) return
#%%registration def register(): #independent network_container() neurite_to_mesh() neurite_bends() neurite_twist() position_global() remesh() scale_radial() spiketrain() #dependent neurite_branches() neuron_neurites() def unregister(): pass