diff --git a/io_mesh_pdb/__init__.py b/io_mesh_pdb/__init__.py index 7cd3d3e6b8f1c53a5e409a75a00a3a2b394aaa32..0a99e7ebfa903f73de5d687de98c3e8ea798d459 100644 --- a/io_mesh_pdb/__init__.py +++ b/io_mesh_pdb/__init__.py @@ -20,7 +20,7 @@ bl_info = { "name": "PDB Atomic Blender", "description": "Loading and manipulating atoms from PDB files", "author": "Clemens Barth", - "version": (1,0), + "version": (1,1), "blender": (2,6), "api": 31236, "location": "File -> Import -> PDB (.pdb), Panel: View 3D - Tools", @@ -119,6 +119,7 @@ class CLASS_atom_pdb_panel(Panel): col = row.column(align=True) col.prop(scn, "atom_pdb_sticks_sectors") col.prop(scn, "atom_pdb_sticks_radius") + col.prop(scn, "use_atom_pdb_sticks_color") row = box.row() row.prop(scn, "use_atom_pdb_center") row = box.row() @@ -129,14 +130,14 @@ class CLASS_atom_pdb_panel(Panel): col.operator("atom_pdb.button_reload") # TODO, use lanel() instead col.prop(scn, "atom_pdb_number_atoms") + row = box.row() + row.operator("atom_pdb.button_distance") + row.prop(scn, "atom_pdb_distance") row = layout.row() row.label(text="Modify atom radii") box = layout.box() row = box.row() - row.operator("atom_pdb.button_distance") - row.prop(scn, "atom_pdb_distance") - row = box.row() row.label(text="All changes concern:") row = box.row() row.prop(scn, "atom_pdb_radius_how") @@ -158,6 +159,11 @@ class CLASS_atom_pdb_panel(Panel): col = row.column(align=True) col.operator( "atom_pdb.radius_all_bigger" ) col.operator( "atom_pdb.radius_all_smaller" ) + row = box.row() + row.label(text="4. Show sticks only") + row = box.row() + col = row.column() + col.operator( "atom_pdb.radius_sticks" ) if bpy.context.mode == 'EDIT_MESH': @@ -221,6 +227,9 @@ class CLASS_atom_pdb_IO(bpy.types.PropertyGroup): scn.atom_pdb_sticks_radius = FloatProperty( name = "Radius", default=0.1, min=0.0, description ="Radius of a stick") + scn.use_atom_pdb_sticks_color = BoolProperty( + name="Color of atoms", default=False, + description="Shall the sticks appear in the color of the atoms?") scn.atom_pdb_atomradius = EnumProperty( name="Type of radius", description="Choose type of atom radius", @@ -301,7 +310,7 @@ class CLASS_atom_pdb_datafile_apply(Operator): return {'FINISHED'} -# Button for measuring the distance of the active objects +# Button for separating single objects from a atom mesh class CLASS_atom_pdb_separate_atom(Operator): bl_idname = "atom_pdb.separate_atom" bl_label = "Separate atom" @@ -426,7 +435,23 @@ class CLASS_atom_pdb_radius_all_smaller_button(Operator): return {'FINISHED'} -# The button for loading the atoms and creating the scene +# Button for showing the sticks only - the radii of the atoms have the radius +# of the sticks +class CLASS_atom_pdb_radius_sticks_button(Operator): + bl_idname = "atom_pdb.radius_sticks" + bl_label = "Show sticks" + bl_description = "Show only the sticks (atom radii = stick radii)" + + def execute(self, context): + scn = bpy.context.scene + import_pdb.DEF_atom_pdb_radius_sticks( + scn.atom_pdb_sticks_radius, + scn.atom_pdb_radius_how, + ) + return {'FINISHED'} + + +# The button for reloading the atoms and creating the scene class CLASS_atom_pdb_load_button(Operator): bl_idname = "atom_pdb.button_reload" bl_label = "RELOAD" @@ -442,6 +467,7 @@ class CLASS_atom_pdb_load_button(Operator): radiustype = scn.atom_pdb_atomradius center = scn.use_atom_pdb_center sticks = scn.use_atom_pdb_sticks + sticks_col = scn.use_atom_pdb_sticks_color ssector = scn.atom_pdb_sticks_sectors sradius = scn.atom_pdb_sticks_radius cam = scn.use_atom_pdb_cam @@ -452,7 +478,7 @@ class CLASS_atom_pdb_load_button(Operator): # Execute main routine an other time ... from the panel atom_number = import_pdb.DEF_atom_pdb_main( mesh, azimuth, zenith, bradius, - radiustype, bdistance, sticks, + radiustype, bdistance, sticks, sticks_col, ssector, sradius, center, cam, lamp, datafile, ) scn.atom_pdb_number_atoms = str(atom_number) + " atoms" @@ -494,6 +520,7 @@ class ImportPDB(Operator, ImportHelper): col = row.column(align=True) col.prop(scn, "atom_pdb_sticks_sectors") col.prop(scn, "atom_pdb_sticks_radius") + col.prop(scn, "use_atom_pdb_sticks_color") row = layout.row() row.prop(scn, "use_atom_pdb_center") @@ -516,6 +543,7 @@ class ImportPDB(Operator, ImportHelper): radiustype = scn.atom_pdb_atomradius center = scn.use_atom_pdb_center sticks = scn.use_atom_pdb_sticks + sticks_col = scn.use_atom_pdb_sticks_color ssector = scn.atom_pdb_sticks_sectors sradius = scn.atom_pdb_sticks_radius cam = scn.use_atom_pdb_cam @@ -526,7 +554,7 @@ class ImportPDB(Operator, ImportHelper): # Execute main routine atom_number = import_pdb.DEF_atom_pdb_main( mesh, azimuth, zenith, bradius, - radiustype, bdistance, sticks, + radiustype, bdistance, sticks, sticks_col, ssector, sradius, center, cam, lamp, datafile) scn.atom_pdb_number_atoms = str(atom_number) + " atoms" diff --git a/io_mesh_pdb/import_pdb.py b/io_mesh_pdb/import_pdb.py index b8da5051166eb4d65b92a55daf45e352da5ef764..78ec2f740b50cb2c01c4e6df7f4ea6dd785a9d6a 100644 --- a/io_mesh_pdb/import_pdb.py +++ b/io_mesh_pdb/import_pdb.py @@ -25,7 +25,7 @@ # # Start of project : 2011-08-31 by Clemens Barth # First publication in Blender : 2011-11-11 -# Last modified : 2011-12-22 +# Last modified : 2011-12-26 # # Acknowledgements: Thanks to ideasman, meta_androcto, truman, kilon, # dairin0d, PKHG, Valter, etc @@ -284,8 +284,7 @@ def DEF_atom_pdb_distance(): return str(dv.length) -# Routine to modify the radii via the type: -# pre-defined, atomic or van der Waals +# Routine to modify the radii via the type: predefined, atomic or van der Waals # Explanations here are also valid for the next 3 DEFs. def DEF_atom_pdb_radius_type(rtype,how): @@ -351,22 +350,26 @@ def DEF_atom_pdb_radius_pm(atomname, radius_pm, how): if len(obj.children) != 0: if obj.children[0].type == "SURFACE" or obj.children[0].type == "MESH": if atomname in obj.name: - obj.children[0].scale = (radius_pm/100,) * 3 + if "Stick" not in obj.name: + obj.children[0].scale = (radius_pm/100,) * 3 else: if obj.type == "SURFACE" or obj.type == "MESH": if atomname in obj.name: - obj.scale = (radius_pm/100,) * 3 + if "Stick" not in obj.name: + obj.scale = (radius_pm/100,) * 3 if how == "ALL_ACTIVE": for obj in bpy.context.selected_objects: if len(obj.children) != 0: if obj.children[0].type == "SURFACE" or obj.children[0].type == "MESH": if atomname in obj.name: - obj.children[0].scale = (radius_pm/100,) * 3 + if "Stick" not in obj.name: + obj.children[0].scale = (radius_pm/100,) * 3 else: if obj.type == "SURFACE" or obj.type == "MESH": if atomname in obj.name: - obj.scale = (radius_pm/100,) * 3 + if "Stick" not in obj.name: + obj.scale = (radius_pm/100,) * 3 # Routine to scale the radii of all atoms @@ -408,6 +411,47 @@ def DEF_atom_pdb_radius_all(scale, how): obj.scale *= scale +# This routine downscales all atom radii onto the value of the stick radius +# for showing the sticks. +def DEF_atom_pdb_radius_sticks(radius, how): + + if how == "ALL_IN_LAYER": + + layers = [] + for i in range(20): + if bpy.context.scene.layers[i] == True: + layers.append(i) + + change_objects = [] + for obj in bpy.context.scene.objects: + for layer in layers: + if obj.layers[layer] == True: + change_objects.append(obj) + + + for obj in change_objects: + if len(obj.children) != 0: + if obj.children[0].type == "SURFACE" or obj.children[0].type == "MESH": + if "Stick" not in obj.name: + obj.children[0].scale = (radius,) * 3 + else: + if obj.type == "SURFACE" or obj.type == "MESH": + if "Stick" not in obj.name: + obj.scale = (radius,) * 3 + + if how == "ALL_ACTIVE": + for obj in bpy.context.selected_objects: + if len(obj.children) != 0: + if obj.children[0].type == "SURFACE" or obj.children[0].type == "MESH": + if "Stick" not in obj.name: + obj.children[0].scale = (radius,) * 3 + else: + if obj.type == "SURFACE" or obj.type == "MESH": + if "Stick" not in obj.name: + obj.scale = (radius,) * 3 + + + # This reads a custom data file. def DEF_atom_pdb_custom_datafile(path_datafile): @@ -476,7 +520,7 @@ def DEF_atom_pdb_custom_datafile(path_datafile): def DEF_atom_pdb_main(use_mesh,Ball_azimuth,Ball_zenith, Ball_radius_factor,radiustype,Ball_distance_factor, - use_stick,Stick_sectors,Stick_diameter,put_to_center, + use_sticks,use_sticks_color,Stick_sectors,Stick_diameter,put_to_center, use_camera,use_lamp,path_datafile): # The list of all atoms as read from the PDB file. @@ -1068,69 +1112,97 @@ def DEF_atom_pdb_main(use_mesh,Ball_azimuth,Ball_zenith, # ------------------------------------------------------------------------ # DRAWING THE STICKS - - if use_stick == True and all_sticks != []: - - # Create a new material with the corresponding color. The - # color is taken from the all_atom list, it is the last entry - # in the data file (index -1). - bpy.ops.object.material_slot_add() - stick_material = bpy.data.materials.new(ATOM_PDB_ELEMENTS[-1].name) - stick_material.diffuse_color = ATOM_PDB_ELEMENTS[-1].color - - vertices = [] - faces = [] - dl = 0.1 - - i = 0 - # For all sticks, do ... - for stick in all_sticks: - + if use_sticks == True and all_sticks != []: + + dl = 0.05 + + if use_sticks_color == False: + bpy.ops.object.material_slot_add() + stick_material = bpy.data.materials.new(ATOM_PDB_ELEMENTS[-1].name) + stick_material.diffuse_color = ATOM_PDB_ELEMENTS[-1].color + + # Sort the sticks and put them into a new list such that ... + sticks_all_lists = [] + if use_sticks_color == True: + for atom_type in atom_all_types_list: + if atom_type[0] == "TER": + continue + sticks_list = [] + for stick in all_sticks: + dv = all_atoms[stick.atom1-1].location - all_atoms[stick.atom2-1].location + n = dv / dv.length + if atom_type[0] == all_atoms[stick.atom1-1].name: + location = all_atoms[stick.atom1-1].location + name = "_" + all_atoms[stick.atom1-1].name + material = all_atoms[stick.atom1-1].material + sticks_list.append([name, location, dv, material]) + if atom_type[0] == all_atoms[stick.atom2-1].name: + location = all_atoms[stick.atom1-1].location - n * dl * int(math.ceil(dv.length / (2.0 * dl))) + name = "_" + all_atoms[stick.atom2-1].name + material = all_atoms[stick.atom2-1].material + sticks_list.append([name, location, dv, material]) + sticks_all_lists.append(sticks_list) + else: + sticks_list = [] + for stick in all_sticks: + dv = all_atoms[stick.atom1-1].location - all_atoms[stick.atom2-1].location + n = dv / dv.length + location = all_atoms[stick.atom1-1].location + material = stick_material + sticks_list.append(["", location, dv, material]) + sticks_all_lists.append(sticks_list) + + + # ... the sticks in the list can be drawn: + for stick_list in sticks_all_lists: + vertices = [] + faces = [] + i = 0 + # What follows is school mathematics! :-) - v1 = all_atoms[stick.atom2-1].location - v2 = all_atoms[stick.atom1-1].location - - dv = (v1 - v2) - - n = dv / dv.length - # m = v1 - dv / 2.0 # UNUSED - - gamma = -n * v1 - b = v1 + gamma * n - n_b = b / b.length - - loops = int(dv.length / dl) - - for j in range(loops): - - g = v1 - n * dl / 2.0 - n * dl * j - - p1 = g + n_b * Stick_diameter - p2 = g - n_b * Stick_diameter - p3 = g - n_b.cross(n) * Stick_diameter - p4 = g + n_b.cross(n) * Stick_diameter - - vertices.append(p1) - vertices.append(p2) - vertices.append(p3) - vertices.append(p4) - faces.append((i*4+0,i*4+2,i*4+1,i*4+3)) - i += 1 - - mesh = bpy.data.meshes.new("Sticks") - mesh.from_pydata(vertices, [], faces) - mesh.update() - new_mesh = bpy.data.objects.new("Sticks", mesh) - bpy.context.scene.objects.link(new_mesh) + for stick in stick_list: + + dv = stick[2] + v1 = stick[1] + n = dv / dv.length + gamma = -n * v1 + b = v1 + gamma * n + n_b = b / b.length + + if use_sticks_color == True: + loops = int(math.ceil(dv.length / (2.0 * dl))) + else: + loops = int(math.ceil(dv.length / dl)) + + for j in range(loops): + + g = v1 - n * dl / 2.0 - n * dl * j + p1 = g + n_b * Stick_diameter + p2 = g - n_b * Stick_diameter + p3 = g - n_b.cross(n) * Stick_diameter + p4 = g + n_b.cross(n) * Stick_diameter + + vertices.append(p1) + vertices.append(p2) + vertices.append(p3) + vertices.append(p4) + faces.append((i*4+0,i*4+2,i*4+1,i*4+3)) + i += 1 + + mesh = bpy.data.meshes.new("Sticks"+stick[0]) + mesh.from_pydata(vertices, [], faces) + mesh.update() + new_mesh = bpy.data.objects.new("Sticks"+stick[0], mesh) + bpy.context.scene.objects.link(new_mesh) - current_layers = bpy.context.scene.layers - stick_cylinder = DEF_atom_pdb_build_stick(Stick_diameter, dl, Stick_sectors) + current_layers = bpy.context.scene.layers + stick_cylinder = DEF_atom_pdb_build_stick(Stick_diameter, dl, Stick_sectors) + stick_cylinder.active_material = stick[3] + stick_cylinder.parent = new_mesh + new_mesh.dupli_type = 'FACES' + atom_object_list.append(new_mesh) - stick_cylinder.active_material = stick_material - stick_cylinder.parent = new_mesh - new_mesh.dupli_type = 'FACES' - atom_object_list.append(new_mesh) # ------------------------------------------------------------------------