ball-and-stick-illustration-1454x150

Blender ball-and-stick figures (and free wallpaper)

Free wallpapers:

neural2d-16x16-8x8-radius-2x2-Bneural2d-4x4-9x4x4neural2d-16x16-8x8-radius-2x2-A

The ball-and-stick illustrations used in the neural2d documentation were made with Blender. This article documents the Python scripting used to generate the connectors (sticks) between the neurons (the spheres) for the benefit of any Blender users who are trying to do something similar.

This script assumes that the sphere objects (neurons) already exist in your Blender scene. You specify the neurons you want connected in the destination layer by selecting the neurons. You specify the neurons you want connected in the source layer by specifying the base part of the object names in the script. Then press Run Script to create all the connection objects.

The green connection objects are bezier curves. I could not figure out how to directly hook each end of a curve to two different objects, so the script hooks each end of the curve to a new Empty, then parents the empties to the source and destination neurons.

After the script has run, you should be able to select and move any neurons, and all their connecting lines will move with them and stay connected as expected.

# Make a ball-and-stick illustration. This script works with Blender 2.74.
# This is part of the neural2d project.
# David R. Miller, 2015; This file is contributed to the public domain.

# Recommended sphere size: about 0.5 blender units.
# Layers are spread along the X axis.
# Nodes within a layer are in the YZ plane.
#
# To use:
#    1. Create the nodes (spheres or whatever, in layers or whatever).
#    2. Set all the nodes' origins to geometry.
#    3. Set sourceObjectsName to the initial part of the names of the source nodes.
#    4. Set connectorThickness, connectorMaterial, and radius if needed.
#    5. Select all the destination nodes.
#    6. Run the script.

import bpy
import time
import math

sourceObjectsName = "input"    # initial part of the source nodes names
connectorThickness = 0.0125
connectorMaterial = "connector"
radius = 999.0   # blender units, YZ plane


def connect(srcNode, dstNode):
    # Add a bezier curve:
    bpy.ops.curve.primitive_bezier_curve_add(location = srcNode.location)
    conn = bpy.context.object
    conn.data.dimensions = '3D'
    conn.data.fill_mode = 'FULL'
    conn.data.bevel_depth = connectorThickness
    conn.data.bevel_resolution = 2
    conn.active_material = bpy.data.materials[connectorMaterial]
    
    # set first control point to centre of srcNode:
    conn.data.splines[0].bezier_points[0].co = conn.matrix_world.inverted() * srcNode.location
    conn.data.splines[0].bezier_points[0].handle_left_type = 'VECTOR'
    conn.data.splines[0].bezier_points[0].handle_right_type = 'VECTOR'

    # set second control point to centre of dstNode:
    conn.data.splines[0].bezier_points[1].co = conn.matrix_world.inverted() * dstNode.location
    conn.data.splines[0].bezier_points[1].handle_left_type = 'VECTOR'
    conn.data.splines[0].bezier_points[1].handle_right_type = 'VECTOR'
    
    # In edit mode, select control point 0 in the connector object, 
    # deselect control point 1 and the handles:
    bpy.ops.object.mode_set(mode='EDIT')
    conn.data.splines[0].bezier_points[0].select_control_point = True
    conn.data.splines[0].bezier_points[1].select_control_point = False
    
    conn.data.splines[0].bezier_points[0].select_left_handle = False
    conn.data.splines[0].bezier_points[0].select_right_handle = False
    
    conn.data.splines[0].bezier_points[1].select_left_handle = False
    conn.data.splines[0].bezier_points[1].select_right_handle = False

    # Hook the end of the connector curve to a new empty:
    bpy.ops.object.hook_add_newob()
    bpy.ops.object.editmode_toggle()
    
    # Parent the new empty to srcNode:
    mt = bpy.data.objects['Empty']
    bpy.context.scene.objects.active = mt
    
    srcNode.select = True
    bpy.context.scene.objects.active = srcNode
    bpy.ops.object.parent_set(type='OBJECT', keep_transform=False)
    
    # Select the connector again:  ----------------------------------
    conn.select = True
    bpy.context.scene.objects.active = conn
    
    # In edit mode, now select the other control point in the connector object, 
    # deselect control point 0:
    bpy.ops.object.mode_set(mode='EDIT')
    conn.data.splines[0].bezier_points[0].select_control_point = False
    conn.data.splines[0].bezier_points[1].select_control_point = True

    # Hook the other end of the connector curve to a new empty:
    bpy.ops.object.hook_add_newob()
    bpy.ops.object.editmode_toggle()
    
    # Parent the empty to dstNode:
    mt = bpy.data.objects['Empty']
    bpy.context.scene.objects.active = mt
    
    dstNode.select = True
    bpy.context.scene.objects.active = dstNode
    bpy.ops.object.parent_set(type='OBJECT', keep_transform=False)



destNodes = bpy.context.selected_objects

# Make a list of the source nodes: (all that begin with sourceObjectsName)

sourceNodes = []

if True:
    for nodeTo in bpy.context.scene.objects:
        if nodeTo.name.startswith(sourceObjectsName):
            sourceNodes.append(nodeTo)


# Create connections:

for srcNode in sourceNodes:
    for dstNode in destNodes:
        y0 = srcNode.location.y
        z0 = srcNode.location.z
        y1 = dstNode.location.y
        z1 = dstNode.location.z
        dist = math.sqrt((z1 - z0) * (z1 - z0) + (y1 - y0) * (y1 - y0))
        if dist < radius:
            connect(srcNode, dstNode)


print("done")

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Before you post, please demonstrate that you are a live, honest person:

How many degrees are in pi radians?