diff --git a/io_mesh_pdb/__init__.py b/io_mesh_pdb/__init__.py index e8ba7a074ac056af6617ef4ccb06bbadbda8fb66..252983935a09f13c7811bdf4ca928d21feaf5db2 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,1), + "version": (1,2), "blender": (2,6), "api": 31236, "location": "File -> Import -> PDB (.pdb), Panel: View 3D - Tools", @@ -58,24 +58,9 @@ class CLASS_atom_pdb_panel(Panel): #bl_context = "physics" # This could be also an option ... : bl_space_type = "VIEW_3D" + #bl_region_type = "TOOLS" bl_region_type = "TOOL_PROPS" - # This 'poll thing' has taken 3 hours of a hard search and understanding. - # I explain it in the following from my point of view: - # - # Before this class is entirely treaten (here: drawing the panel) the - # poll method is called first. Basically, some conditions are - # checked before other things in the class are done afterwards. If a - # condition is not valid, one returns 'False' such that nothing further - # is done. 'True' means: 'Go on' - # - # In the case here, it is verified if the ATOM_PDB_FILEPATH variable contains - # a name. If not - and this is the case directly after having started the - # script - the panel does not appear because 'False' is returned. However, - # as soon as a file has been chosen, the panel appears because - # ATOM_PDB_FILEPATH contains a name. - # - # Please, correct me if I'm wrong. @classmethod def poll(self, context): if import_pdb.ATOM_PDB_FILEPATH == "": @@ -106,21 +91,36 @@ class CLASS_atom_pdb_panel(Panel): box = layout.box() row = box.row() - col = row.column(align=True) + col = row.column() col.prop(scn, "use_atom_pdb_mesh") + col = row.column() + col.label(text="Scaling factors") + row = box.row() + col = row.column(align=True) + col.active = scn.use_atom_pdb_mesh col.prop(scn, "atom_pdb_mesh_azimuth") col.prop(scn, "atom_pdb_mesh_zenith") col = row.column(align=True) - col.label(text="Scaling factors") col.prop(scn, "atom_pdb_scale_ballradius") col.prop(scn, "atom_pdb_scale_distances") row = box.row() - col = row.column() + col = row.column() col.prop(scn, "use_atom_pdb_sticks") + row = box.row() + row.active = scn.use_atom_pdb_sticks col = row.column(align=True) col.prop(scn, "atom_pdb_sticks_sectors") col.prop(scn, "atom_pdb_sticks_radius") + col = row.column(align=True) col.prop(scn, "use_atom_pdb_sticks_color") + col.prop(scn, "use_atom_pdb_sticks_smooth") + col.prop(scn, "use_atom_pdb_sticks_bonds") + row = box.row() + row.active = scn.use_atom_pdb_sticks + col = row.column(align=True) + col = row.column(align=True) + col.active = scn.use_atom_pdb_sticks and scn.use_atom_pdb_sticks_bonds + col.prop(scn, "atom_pdb_sticks_dist") row = box.row() row.prop(scn, "use_atom_pdb_center") row = box.row() @@ -129,7 +129,6 @@ class CLASS_atom_pdb_panel(Panel): col.prop(scn, "use_atom_pdb_lamp") col = row.column() 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") @@ -137,6 +136,7 @@ class CLASS_atom_pdb_panel(Panel): row = layout.row() row.label(text="Modify atom radii") + box = layout.box() row = box.row() row.label(text="All changes concern:") @@ -219,7 +219,7 @@ class CLASS_atom_pdb_IO(bpy.types.PropertyGroup): name = "Object to origin", default=True, description = "Put the object into the global origin") scn.use_atom_pdb_sticks = BoolProperty( - name="Use sticks", default=False, + name="Use sticks", default=True, description="Do you want to display the sticks?") scn.atom_pdb_sticks_sectors = IntProperty( name = "Sector", default=20, min=0, @@ -228,8 +228,17 @@ class CLASS_atom_pdb_IO(bpy.types.PropertyGroup): name = "Radius", default=0.1, min=0.0, description ="Radius of a stick") scn.use_atom_pdb_sticks_color = BoolProperty( - name="Color", default=False, + name="Color", default=True, description="The sticks appear in the color of the atoms") + scn.use_atom_pdb_sticks_smooth = BoolProperty( + name="Smooth", default=False, + description="The sticks are round (sectors are not visible)") + scn.use_atom_pdb_sticks_bonds = BoolProperty( + name="Bonds", default=False, + description="Show double and tripple bonds.") + scn.atom_pdb_sticks_dist = FloatProperty( + name="Distance", default = 1.1, min=1.0, max=3.0, + description="Distance between sticks measured in stick diameter") scn.atom_pdb_atomradius = EnumProperty( name="Type of radius", description="Choose type of atom radius", @@ -243,9 +252,8 @@ class CLASS_atom_pdb_IO(bpy.types.PropertyGroup): name = "", description="Path to your custom data file", maxlen = 256, default = "", subtype='FILE_PATH') scn.atom_pdb_PDB_file = StringProperty( - name = "Path to file", default="", + name = "PDB file", default="", description = "Path of the PDB file") - # TODO, remove this property, its used for display only! scn.atom_pdb_number_atoms = StringProperty(name="", default="Number", description = "This output shows " "the number of atoms which have been loaded") @@ -435,8 +443,8 @@ class CLASS_atom_pdb_radius_all_smaller_button(Operator): return {'FINISHED'} -# Button for showing the sticks only - the radii of the atoms have the radius -# of the sticks +# Button for showing the sticks only - the radii of the atoms downscaled onto +# 90% of the stick radius class CLASS_atom_pdb_radius_sticks_button(Operator): bl_idname = "atom_pdb.radius_sticks" bl_label = "Show sticks" @@ -448,7 +456,7 @@ class CLASS_atom_pdb_radius_sticks_button(Operator): scn = bpy.context.scene result = import_pdb.DEF_atom_pdb_radius_sticks( - scn.atom_pdb_sticks_radius, + scn.atom_pdb_sticks_radius * 0.9, scn.atom_pdb_radius_how, ) @@ -476,19 +484,22 @@ class CLASS_atom_pdb_load_button(Operator): center = scn.use_atom_pdb_center sticks = scn.use_atom_pdb_sticks sticks_col = scn.use_atom_pdb_sticks_color + sticks_sm = scn.use_atom_pdb_sticks_smooth ssector = scn.atom_pdb_sticks_sectors sradius = scn.atom_pdb_sticks_radius + stick_bond = scn.use_atom_pdb_sticks_bonds + stick_dist = scn.atom_pdb_sticks_dist + cam = scn.use_atom_pdb_cam lamp = scn.use_atom_pdb_lamp mesh = scn.use_atom_pdb_mesh datafile = scn.atom_pdb_datafile - + # Execute main routine an other time ... from the panel atom_number = import_pdb.DEF_atom_pdb_main( - mesh, azimuth, zenith, bradius, - radiustype, bdistance, sticks, sticks_col, - ssector, sradius, center, cam, lamp, datafile, - ) + mesh, azimuth, zenith, bradius, radiustype, bdistance, + sticks, sticks_col, sticks_sm, stick_bond, + stick_dist, ssector, sradius, center, cam, lamp, datafile) scn.atom_pdb_number_atoms = str(atom_number) + " atoms" return {'FINISHED'} @@ -513,6 +524,7 @@ class ImportPDB(Operator, ImportHelper): col = row.column() col.prop(scn, "use_atom_pdb_mesh") col = row.column(align=True) + col.active = scn.use_atom_pdb_mesh col.prop(scn, "atom_pdb_mesh_azimuth") col.prop(scn, "atom_pdb_mesh_zenith") @@ -525,10 +537,21 @@ class ImportPDB(Operator, ImportHelper): row = layout.row() col = row.column() col.prop(scn, "use_atom_pdb_sticks") + row = layout.row() + row.active = scn.use_atom_pdb_sticks col = row.column(align=True) col.prop(scn, "atom_pdb_sticks_sectors") col.prop(scn, "atom_pdb_sticks_radius") + col = row.column(align=True) col.prop(scn, "use_atom_pdb_sticks_color") + col.prop(scn, "use_atom_pdb_sticks_smooth") + col.prop(scn, "use_atom_pdb_sticks_bonds") + row = layout.row() + row.active = scn.use_atom_pdb_sticks + col = row.column(align=True) + col = row.column(align=True) + col.active = scn.use_atom_pdb_sticks and scn.use_atom_pdb_sticks_bonds + col.prop(scn, "atom_pdb_sticks_dist") row = layout.row() row.prop(scn, "use_atom_pdb_center") @@ -552,18 +575,22 @@ class ImportPDB(Operator, ImportHelper): center = scn.use_atom_pdb_center sticks = scn.use_atom_pdb_sticks sticks_col = scn.use_atom_pdb_sticks_color + sticks_sm = scn.use_atom_pdb_sticks_smooth ssector = scn.atom_pdb_sticks_sectors sradius = scn.atom_pdb_sticks_radius + stick_bond = scn.use_atom_pdb_sticks_bonds + stick_dist = scn.atom_pdb_sticks_dist + cam = scn.use_atom_pdb_cam lamp = scn.use_atom_pdb_lamp mesh = scn.use_atom_pdb_mesh datafile = scn.atom_pdb_datafile - + # Execute main routine atom_number = import_pdb.DEF_atom_pdb_main( - mesh, azimuth, zenith, bradius, - radiustype, bdistance, sticks, sticks_col, - ssector, sradius, center, cam, lamp, datafile) + mesh, azimuth, zenith, bradius, radiustype, bdistance, + sticks, sticks_col, sticks_sm, stick_bond, + stick_dist, 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 d007603f88f713c6ed81b8ad8e66f5ae948883b5..cf559d9187b287a058760f95c8570244b39b2e8f 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-26 +# Last modified : 2011-12-30 # # Acknowledgements: Thanks to ideasman, meta_androcto, truman, kilon, # dairin0d, PKHG, Valter, etc @@ -35,8 +35,10 @@ import bpy import io import math import os +import copy from math import pi, cos, sin from mathutils import Vector, Matrix +from copy import copy # These are variables, which contain the name of the PDB file and # the path of the PDB file. @@ -207,10 +209,12 @@ class CLASS_atom_pdb_atom(object): # This is the class, which stores the two atoms of one stick. class CLASS_atom_pdb_stick(object): - __slots__ = ('atom1', 'atom2') - def __init__(self, atom1, atom2): + __slots__ = ('atom1', 'atom2', 'number', 'dist') + def __init__(self, atom1, atom2, number, dist): self.atom1 = atom1 self.atom2 = atom2 + self.number = number + self.dist = dist # ----------------------------------------------------------------------------- @@ -492,7 +496,9 @@ def DEF_atom_pdb_radius_sticks(radius, how): return True -# This reads a custom data file. +# ----------------------------------------------------------------------------- +# The custom data file + def DEF_atom_pdb_custom_datafile(path_datafile): if path_datafile == "": @@ -560,7 +566,9 @@ 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_sticks,use_sticks_color,Stick_sectors,Stick_diameter,put_to_center, + use_sticks,use_sticks_color,use_sticks_smooth, + use_sticks_bonds,Stick_dist, + Stick_sectors,Stick_diameter,put_to_center, use_camera,use_lamp,path_datafile): # The list of all atoms as read from the PDB file. @@ -849,10 +857,34 @@ def DEF_atom_pdb_main(use_mesh,Ball_azimuth,Ball_zenith, atom1 = atom_list[0] # For all the other atoms in the list do: - for each_atom in atom_list[1:]: - - # The second, third, ... partner atom - atom2 = each_atom + for atom2 in atom_list[1:]: + + if use_sticks_bonds == True: + number = atom_list[1:].count(atom2) + if number == 2 or number == 3: + basis_list = list(set(atom_list[1:])) + + if len(basis_list) > 1: + basis1 = (all_atoms[atom1-1].location + - all_atoms[basis_list[0]-1].location) + basis2 = (all_atoms[atom1-1].location + - all_atoms[basis_list[1]-1].location) + plane_n = basis1.cross(basis2) + + dist_n = (all_atoms[atom1-1].location + - all_atoms[atom2-1].location) + dist_n = dist_n.cross(plane_n) + dist_n = dist_n / dist_n.length + else: + dist_n = Vector((0,0,0)) + elif number > 3: + number = 1 + dist_n = None + else: + dist_n = None + else: + number = 1 + dist_n = None # Note that in a PDB file, sticks of one atom pair can appear a # couple of times. (Only god knows why ...) @@ -869,7 +901,7 @@ def DEF_atom_pdb_main(use_mesh,Ball_azimuth,Ball_zenith, # If the stick is not yet registered (FLAG_BAR == False), then # register it! if FLAG_BAR == False: - all_sticks.append(CLASS_atom_pdb_stick(atom1,atom2)) + all_sticks.append(CLASS_atom_pdb_stick(atom1,atom2,number,dist_n)) Number_of_sticks += 1 j += 1 @@ -1169,27 +1201,79 @@ def DEF_atom_pdb_main(use_mesh,Ball_azimuth,Ball_zenith, 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]) + + for repeat in range(stick.number): + + atom1 = copy(all_atoms[stick.atom1-1].location) + atom2 = copy(all_atoms[stick.atom2-1].location) + + dist = Stick_diameter * Stick_dist + + if stick.number == 2: + if repeat == 0: + atom1 += (stick.dist * dist) + atom2 += (stick.dist * dist) + if repeat == 1: + atom1 -= (stick.dist * dist) + atom2 -= (stick.dist * dist) + + if stick.number == 3: + if repeat == 0: + atom1 += (stick.dist * dist) + atom2 += (stick.dist * dist) + if repeat == 2: + atom1 -= (stick.dist * dist) + atom2 -= (stick.dist * dist) + + dv = atom1 - atom2 + n = dv / dv.length + if atom_type[0] == all_atoms[stick.atom1-1].name: + location = atom1 + 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 = atom1 - 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]) + + if stick.number > 3: + stick.number = 1 + + for repeat in range(stick.number): + + atom1 = copy(all_atoms[stick.atom1-1].location) + atom2 = copy(all_atoms[stick.atom2-1].location) + + dist = Stick_diameter * Stick_dist + + if stick.number == 2: + if repeat == 0: + atom1 += (stick.dist * dist) + atom2 += (stick.dist * dist) + if repeat == 1: + atom1 -= (stick.dist * dist) + atom2 -= (stick.dist * dist) + if stick.number == 3: + if repeat == 0: + atom1 += (stick.dist * dist) + atom2 += (stick.dist * dist) + if repeat == 2: + atom1 -= (stick.dist * dist) + atom2 -= (stick.dist * dist) + + dv = atom1 - atom2 + n = dv / dv.length + location = atom1 + material = stick_material + sticks_list.append(["", location, dv, material]) + sticks_all_lists.append(sticks_list) @@ -1238,8 +1322,12 @@ def DEF_atom_pdb_main(use_mesh,Ball_azimuth,Ball_zenith, 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 + + if use_sticks_smooth == True: + for face in stick_cylinder.data.faces: + face.use_smooth = True + stick_cylinder.parent = new_mesh new_mesh.dupli_type = 'FACES' atom_object_list.append(new_mesh)