From 421d727de5be048774ffdefcb3401d56be34e94b Mon Sep 17 00:00:00 2001 From: Pierre Ratinaud Date: Tue, 12 Jul 2016 15:29:11 +0200 Subject: [PATCH] network to blender --- network_to_blender.py | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 network_to_blender.py diff --git a/network_to_blender.py b/network_to_blender.py new file mode 100644 index 0000000..8972ffc --- /dev/null +++ b/network_to_blender.py @@ -0,0 +1,213 @@ +""" +Blender script. Draws a node-and-edge network in blender, randomly distributed +spherically. + +14 Sept 2011: Added collision detection between nodes + +30 Nov 2012: Rewrote. Switched to JSON, and large Blender speed boosts. + +Written by Patrick Fuller, patrickfuller@gmail.com, 11 Sept 11 + +modifications by Pierre Ratinaud Feb 2014 +""" +import bpy +from math import acos, degrees, pi +from mathutils import Vector +from copy import copy + +import json +from random import choice +import sys + +# Colors to turn into materials +#colors = {"purple": (178, 132, 234), "gray": (11, 11, 11), +# "green": (114, 195, 0), "red": (255, 0, 75), +# "blue": (0, 131, 255), "clear": (0, 131, 255), +# "yellow": (255, 187, 0), "light_gray": (118, 118, 118)} + + +# Normalize to [0,1] and make blender materials +def make_colors(colors): + for key, value in colors.items(): + value = [x / 255.0 for x in value] + bpy.data.materials.new(name=key) + bpy.data.materials[key].diffuse_color = value + bpy.data.materials[key].specular_intensity = 0.5 + + # Don't specify more parameters if these colors + if key == "gray" or key == "light_gray": + bpy.data.materials[key].use_transparency = True + bpy.data.materials[key].transparency_method = "Z_TRANSPARENCY" + bpy.data.materials[key].alpha = 0.2 + + # Transparency parameters + else : + bpy.data.materials[key].use_transparency = True + bpy.data.materials[key].transparency_method = "Z_TRANSPARENCY" + bpy.data.materials[key].alpha = 0.6 if key == "clear" else 0.8 + bpy.data.materials.new(name = key + 'sphere') + bpy.data.materials[key + 'sphere'].diffuse_color = value + bpy.data.materials[key + 'sphere'].specular_intensity = 0.1 + bpy.data.materials[key + 'sphere'].use_transparency = True + bpy.data.materials[key + 'sphere'].transparency_method = "Z_TRANSPARENCY" + bpy.data.materials[key + 'sphere'].alpha = 0.1 + #bpy.data.materials[key].raytrace_transparency.fresnel = 0.1 + #bpy.data.materials[key].raytrace_transparency.ior = 1.15 + + +def draw_network(network, edge_thickness=0.25, node_size=3, directed=False, spheres = True): + """ Takes assembled network/molecule data and draws to blender """ + + colors = [tuple(network["nodes"][node]['color']) for node in network["nodes"]] + cols = list(set(colors)) + colors = dict(zip([str(col) for col in cols],cols)) + colors.update({"light_gray": (118, 118, 118), "gray": (11, 11, 11)}) + make_colors(colors) + + # Add some mesh primitives + bpy.ops.object.select_all(action='DESELECT') + #bpy.ops.mesh.primitive_uv_sphere_add() + bpy.ops.mesh.primitive_uv_sphere_add(segments = 64, ring_count = 32) + sphere = bpy.context.object + bpy.ops.mesh.primitive_cylinder_add() + cylinder = bpy.context.object + cylinder.active_material = bpy.data.materials["light_gray"] + bpy.ops.mesh.primitive_cone_add() + cone = bpy.context.object + cone.active_material = bpy.data.materials["light_gray"] + #bpy.ops.object.text_add(view_align=True) + + + # Keep references to all nodes and edges + shapes = [] + # Keep separate references to shapes to be smoothed + shapes_to_smooth = [] + #val to div coordonnate + divval = 0.1 + + # Draw nodes + for key, node in network["nodes"].items(): + + # Coloring rule for nodes. Edit this to suit your needs! + col = str(tuple(node.get("color", choice(list(colors.keys()))))) + + # Copy mesh primitive and edit to make node + # (You can change the shape of drawn nodes here) + if spheres : + node_sphere = sphere.copy() + node_sphere.data = sphere.data.copy() + node_sphere.location = [val/divval for val in node["location"]] + #node_sphere.dimensions = [node_size] * 3 + node_sphere.dimensions = [node["weight"]/10] * 3 + #newmat = bpy.data.materials[col] + #newmat.alpha = 0.01 + node_sphere.active_material = bpy.data.materials[col + 'sphere'] + bpy.context.scene.objects.link(node_sphere) + shapes.append(node_sphere) + shapes_to_smooth.append(node_sphere) + + #node_text = text.copy() + #node_text.data = text.data.copy() + #node_text.location = node["location"] + bpy.ops.object.text_add(view_align=False, location = [val/divval for val in node["location"]]) + #bpy.ops.object.text_add(view_align=False, location = [val for val in node["location"]]) + bpy.ops.object.editmode_toggle() + bpy.ops.font.delete() + bpy.ops.font.text_insert(text=key) + + bpy.ops.object.editmode_toggle() + bpy.data.curves[bpy.context.active_object.name].size = node["weight"] /10 + bpy.data.curves[bpy.context.active_object.name].bevel_depth = 0.044 + bpy.data.curves[bpy.context.active_object.name].offset = 0 + bpy.data.curves[bpy.context.active_object.name].extrude = 0.2 + bpy.data.curves[bpy.context.active_object.name].align = "CENTER" + bpy.context.active_object.rotation_euler = [1.5708,0,1.5708] + bpy.context.active_object.active_material = bpy.data.materials[col] + const = bpy.context.active_object.constraints.new(type='TRACK_TO') + const.target = bpy.data.objects['Camera'] + const.track_axis = "TRACK_Z" + const.up_axis = "UP_Y" + + #bpy.context.scene.objects.link(bpy.context.active_object) + #shapes.append(bpy.context.active_object) + #sha* 2 + [mag - node_size] + shapes_to_smooth.append(bpy.context.active_object) + + # Draw edges + for edge in network["edges"]: + + # Get source and target locations by drilling down into data structure + source_loc = network["nodes"][edge["source"]]["location"] + source_loc = [val/divval for val in source_loc] + target_loc = network["nodes"][edge["target"]]["location"] + target_loc = [val / divval for val in target_loc] + + diff = [c2 - c1 for c2, c1 in zip(source_loc, target_loc)] + cent = [(c2 + c1) / 2 for c2, c1 in zip(source_loc, target_loc)] + mag = sum([(c2 - c1) ** 2 + for c1, c2 in zip(source_loc, target_loc)]) ** 0.5 + + # Euler rotation calculation + v_axis = Vector(diff).normalized() + v_obj = Vector((0, 0, 1)) + v_rot = v_obj.cross(v_axis) + angle = acos(v_obj.dot(v_axis)) + + # Copy mesh primitive to create edge + edge_cylinder = cylinder.copy() + edge_cylinder.data = cylinder.data.copy() + edge_cylinder.dimensions = [float(edge['weight'])*10] * 2 + [mag - node_size] + #edge_cylinder.dimensions = [edge_thickness] * 2 + [mag - node_size] + edge_cylinder.location = cent + edge_cylinder.rotation_mode = "AXIS_ANGLE" + edge_cylinder.rotation_axis_angle = [angle] + list(v_rot) + bpy.context.scene.objects.link(edge_cylinder) + shapes.append(edge_cylinder) + shapes_to_smooth.append(edge_cylinder) + + # Copy another mesh primitive to make an arrow head + if directed: + arrow_cone = cone.copy() + arrow_cone.data = cone.data.copy() + arrow_cone.dimensions = [edge_thickness * 4.0] * 3 + arrow_cone.location = cent + arrow_cone.rotation_mode = "AXIS_ANGLE" + arrow_cone.rotation_axis_angle = [angle + pi] + list(v_rot) + bpy.context.scene.objects.link(arrow_cone) + shapes.append(arrow_cone) + + # Remove primitive meshes + bpy.ops.object.select_all(action='DESELECT') + sphere.select = True + cylinder.select = True + cone.select = True + #text.select = True + + # If the starting cube is there, remove it + if "Cube" in bpy.data.objects.keys(): + bpy.data.objects.get("Cube").select = True + bpy.ops.object.delete() + + # Smooth specified shapes + for shape in shapes_to_smooth: + shape.select = True + #bpy.context.scene.objects.active = shapes_to_smooth[0] + #bpy.ops.object.shade_smooth() + + # Join shapes + for shape in shapes: + shape.select = True + #bpy.context.scene.objects.active = shapes[0] + #bpy.ops.object.join() + + # Center object origin to geometry + bpy.ops.object.origin_set(type="ORIGIN_GEOMETRY", center="MEDIAN") + + # Refresh scene + bpy.context.scene.update() + +# If main, load json and run +if __name__ == "__main__": + with open(sys.argv[3]) as network_file: + network = json.load(network_file) + draw_network(network) -- 2.7.4