From 4a26b71a377679c0f5ff0ed7e5ee01db71446454 Mon Sep 17 00:00:00 2001 From: Clemens Barth <barth@root-1.de> Date: Sat, 3 Nov 2012 19:38:28 +0000 Subject: [PATCH] As discussed a couple of times in the IRC chat, we decided to separate the panel from the import/export addons. This means that the panel has had to be removed also from the PDB importer/exporter. This is what I have done. Attention, this is a major change of the Atomic Blender PDB importer/exporter! A lot of test today have shown, that the new version of the PDB importer/exporter has not lost its stability. In the next days, I will perform other stability tests. Blendphys --- io_mesh_pdb/__init__.py | 553 +---------------------------- io_mesh_pdb/import_pdb.py | 718 +++++++++++--------------------------- 2 files changed, 223 insertions(+), 1048 deletions(-) diff --git a/io_mesh_pdb/__init__.py b/io_mesh_pdb/__init__.py index 580fffa08..22e4e7104 100644 --- a/io_mesh_pdb/__init__.py +++ b/io_mesh_pdb/__init__.py @@ -21,23 +21,26 @@ # Authors : Clemens Barth (Blendphys@root-1.de), ... # # Homepage(Wiki) : http://development.root-1.de/Atomic_Blender.php -# Tracker : http://projects.blender.org/tracker/index.php?func=detail&aid=29226&group_id=153&atid=467 # # Start of project : 2011-08-31 by Clemens Barth # First publication in Blender : 2011-11-11 -# Last modified : 2012-10-14 +# Last modified : 2012-11-03 # -# Acknowledgements: Thanks to ideasman, meta_androcto, truman, kilon, -# dairin0d, PKHG, Valter, etc +# Acknowledgements +# ================ +# +# Blender: ideasman, meta_androcto, truman, kilon, CoDEmanX, dairin0d, PKHG, +# Valter, ... +# Other: Frank Palmino # bl_info = { - "name": "PDB Atomic Blender", + "name": "Atomic Blender - PDB", "description": "Loading and manipulating atoms from PDB files", "author": "Clemens Barth", - "version": (1,3), + "version": (1,5), "blender": (2,6), - "location": "File -> Import -> PDB (.pdb), Panel: View 3D - Tools", + "location": "File -> Import -> PDB (.pdb)", "warning": "", "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/" "Py/Scripts/Import-Export/PDB", @@ -61,501 +64,9 @@ from bpy.props import (StringProperty, from . import import_pdb from . import export_pdb -ATOM_PDB_ERROR = "" -ATOM_PDB_PANEL = "" - # ----------------------------------------------------------------------------- # GUI -# The panel, which is loaded after the file has been -# chosen via the menu 'File -> Import' -class CLASS_atom_pdb_panel(Panel): - bl_label = "PDB - Atomic Blender" - bl_space_type = "VIEW_3D" - bl_region_type = "TOOL_PROPS" - - @classmethod - def poll(self, context): - global ATOM_PDB_PANEL - - if ATOM_PDB_PANEL == "0" and import_pdb.ATOM_PDB_FILEPATH == "": - return False - if ATOM_PDB_PANEL == "0" and import_pdb.ATOM_PDB_FILEPATH != "": - return True - if ATOM_PDB_PANEL == "1": - return True - if ATOM_PDB_PANEL == "2": - return False - - return True - - def draw(self, context): - layout = self.layout - - # This is for the case that a blend file is loaded. - if len(context.scene.atom_pdb) == 0: - bpy.context.scene.atom_pdb.add() - - scn = context.scene.atom_pdb[0] - - row = layout.row() - row.label(text="Outputs and custom data file") - box = layout.box() - row = box.row() - row.label(text="Custom data file") - row = box.row() - col = row.column() - col.prop(scn, "datafile") - col.operator("atom_pdb.datafile_apply") - row = box.row() - col = row.column(align=True) - col.prop(scn, "PDB_file") - row = layout.row() - row.label(text="Reload structure") - box = layout.box() - row = box.row() - col = row.column() - col.prop(scn, "use_mesh") - col = row.column() - col.label(text="Scaling factors") - row = box.row() - col = row.column(align=True) - col.active = scn.use_mesh - col.prop(scn, "mesh_azimuth") - col.prop(scn, "mesh_zenith") - col = row.column(align=True) - col.prop(scn, "scale_ballradius") - col.prop(scn, "scale_distances") - row = box.row() - col = row.column() - col.prop(scn, "use_sticks") - row = box.row() - row.active = scn.use_sticks - col = row.column(align=True) - col.prop(scn, "sticks_sectors") - col.prop(scn, "sticks_radius") - col.prop(scn, "sticks_unit_length") - col = row.column(align=True) - col.prop(scn, "use_sticks_color") - col.prop(scn, "use_sticks_smooth") - col.prop(scn, "use_sticks_bonds") - row = box.row() - row.active = scn.use_sticks - col = row.column(align=True) - col = row.column(align=True) - col.active = scn.use_sticks and scn.use_sticks_bonds - col.prop(scn, "sticks_dist") - row = box.row() - row.prop(scn, "use_center") - row = box.row() - col = row.column() - col.prop(scn, "use_camera") - col.prop(scn, "use_lamp") - col = row.column() - col.operator("atom_pdb.button_reload") - col.prop(scn, "number_atoms") - row = box.row() - row.operator("atom_pdb.button_distance") - row.prop(scn, "distance") - row = layout.row() - row.label(text="Modify atom radii") - box = layout.box() - row = box.row() - row.label(text="All changes concern:") - row = box.row() - row.prop(scn, "radius_how") - row = box.row() - row.label(text="1. Change type of radii") - row = box.row() - row.prop(scn, "radius_type") - row = box.row() - row.label(text="2. Change atom radii in pm") - row = box.row() - row.prop(scn, "radius_pm_name") - row = box.row() - row.prop(scn, "radius_pm") - row = box.row() - row.label(text="3. Change atom radii by scale") - row = box.row() - col = row.column() - col.prop(scn, "radius_all") - 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': - row = layout.row() - row.label(text="Separate atom") - box = layout.box() - row = box.row() - row.operator( "atom_pdb.separate_atom" ) - - -# The properties (gadgets) in the panel. They all go to scene -# during initialization (see end) -class CLASS_atom_pdb_Properties(bpy.types.PropertyGroup): - - def Callback_radius_type(self, context): - scn = bpy.context.scene.atom_pdb[0] - import_pdb.DEF_atom_pdb_radius_type( - scn.radius_type, - scn.radius_how,) - - def Callback_radius_pm(self, context): - scn = bpy.context.scene.atom_pdb[0] - import_pdb.DEF_atom_pdb_radius_pm( - scn.radius_pm_name, - scn.radius_pm, - scn.radius_how,) - - # In the file dialog window - Import - use_camera = BoolProperty( - name="Camera", default=False, - description="Do you need a camera?") - use_lamp = BoolProperty( - name="Lamp", default=False, - description = "Do you need a lamp?") - use_mesh = BoolProperty( - name = "Mesh balls", default=False, - description = "Use mesh balls instead of NURBS") - mesh_azimuth = IntProperty( - name = "Azimuth", default=32, min=1, - description = "Number of sectors (azimuth)") - mesh_zenith = IntProperty( - name = "Zenith", default=32, min=1, - description = "Number of sectors (zenith)") - scale_ballradius = FloatProperty( - name = "Balls", default=1.0, min=0.0001, - description = "Scale factor for all atom radii") - scale_distances = FloatProperty ( - name = "Distances", default=1.0, min=0.0001, - description = "Scale factor for all distances") - use_center = BoolProperty( - name = "Object to origin", default=True, - description = "Put the object into the global origin") - use_sticks = BoolProperty( - name="Use sticks", default=True, - description="Do you want to display the sticks?") - sticks_sectors = IntProperty( - name = "Sector", default=20, min=1, - description="Number of sectors of a stick") - sticks_radius = FloatProperty( - name = "Radius", default=0.1, min=0.0001, - description ="Radius of a stick") - sticks_unit_length = FloatProperty( - name = "Unit", default=0.05, min=0.0001, - description = "Length of the unit of a stick in Angstrom") - use_sticks_color = BoolProperty( - name="Color", default=True, - description="The sticks appear in the color of the atoms") - use_sticks_smooth = BoolProperty( - name="Smooth", default=False, - description="The sticks are round (sectors are not visible)") - use_sticks_bonds = BoolProperty( - name="Bonds", default=False, - description="Show double and tripple bonds.") - sticks_dist = FloatProperty( - name="Distance", default = 1.1, min=1.0, max=3.0, - description="Distance between sticks measured in stick diameter") - atomradius = EnumProperty( - name="Type of radius", - description="Choose type of atom radius", - items=(('0', "Pre-defined", "Use pre-defined radius"), - ('1', "Atomic", "Use atomic radius"), - ('2', "van der Waals", "Use van der Waals radius")), - default='0',) - # In the panel - datafile = StringProperty( - name = "", description="Path to your custom data file", - maxlen = 256, default = "", subtype='FILE_PATH') - PDB_file = StringProperty( - name = "PDB file", default="", - description = "Path of the PDB file") - number_atoms = StringProperty(name="", - default="Number", description = "This output shows " - "the number of atoms which have been loaded") - distance = StringProperty( - name="", default="Distance (A)", - description="Distance of 2 objects in Angstrom") - radius_how = EnumProperty( - name="", - description="Which objects shall be modified?", - items=(('ALL_ACTIVE',"all active objects", "in the current layer"), - ('ALL_IN_LAYER',"all"," in active layer(s)")), - default='ALL_ACTIVE',) - radius_type = EnumProperty( - name="Type", - description="Which type of atom radii?", - items=(('0',"predefined", "Use pre-defined radii"), - ('1',"atomic", "Use atomic radii"), - ('2',"van der Waals","Use van der Waals radii")), - default='0',update=Callback_radius_type) - radius_pm_name = StringProperty( - name="", default="Atom name", - description="Put in the name of the atom (e.g. Hydrogen)") - radius_pm = FloatProperty( - name="", default=100.0, min=0.01, - description="Put in the radius of the atom (in pm)", - update=Callback_radius_pm) - radius_all = FloatProperty( - name="Scale", default = 1.05, min=1.0, max=5.0, - description="Put in the scale factor") - - -# Button loading a custom data file -class CLASS_atom_pdb_datafile_apply(Operator): - bl_idname = "atom_pdb.datafile_apply" - bl_label = "Apply" - bl_description = "Use color and radii values stored in the custom file" - - def execute(self, context): - scn = bpy.context.scene.atom_pdb[0] - - if scn.datafile == "": - return {'FINISHED'} - - import_pdb.DEF_atom_pdb_custom_datafile(scn.datafile) - - return {'FINISHED'} - - -# 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" - bl_description = "Separate the atom you have chosen" - - def execute(self, context): - scn = bpy.context.scene.atom_pdb[0] - - # Get first all important properties from the atoms, which the user - # has chosen: location, color, scale - obj = bpy.context.edit_object - bm = bmesh.from_edit_mesh(obj.data) - - locations = [] - - for v in bm.verts: - if v.select: - locations.append(obj.matrix_world * v.co) - - bm.free() - del(bm) - - name = obj.name - scale = obj.children[0].scale - material = obj.children[0].active_material - - # Separate the vertex from the main mesh and create a new mesh. - bpy.ops.mesh.separate() - new_object = bpy.context.scene.objects[0] - # And now, switch to the OBJECT mode such that we can ... - bpy.ops.object.mode_set(mode='OBJECT', toggle=False) - # ... delete the new mesh including the separated vertex - bpy.ops.object.select_all(action='DESELECT') - new_object.select = True - bpy.ops.object.delete() - - # Create new atoms/vacancies at the position of the old atoms - current_layers=bpy.context.scene.layers - - # For all selected positions do: - for location in locations: - if "Vacancy" not in name: - if scn.use_mesh == False: - bpy.ops.surface.primitive_nurbs_surface_sphere_add( - view_align=False, enter_editmode=False, - location=location, - rotation=(0.0, 0.0, 0.0), - layers=current_layers) - else: - bpy.ops.mesh.primitive_uv_sphere_add( - segments=scn.mesh_azimuth, - ring_count=scn.mesh_zenith, - size=1, view_align=False, enter_editmode=False, - location=location, - rotation=(0, 0, 0), - layers=current_layers) - else: - bpy.ops.mesh.primitive_cube_add( - view_align=False, enter_editmode=False, - location=location, - rotation=(0.0, 0.0, 0.0), - layers=current_layers) - - new_atom = bpy.context.scene.objects.active - # Scale, material and name it. - new_atom.scale = scale - new_atom.active_material = material - new_atom.name = name + "_sep" - new_atom.select = False - - bpy.context.scene.objects.active = obj - bpy.ops.object.select_all(action='DESELECT') - - return {'FINISHED'} - - -# Button for measuring the distance of the active objects -class CLASS_atom_pdb_distance_button(Operator): - bl_idname = "atom_pdb.button_distance" - bl_label = "Measure ..." - bl_description = "Measure the distance between two objects (only in Object Mode)" - - def execute(self, context): - scn = bpy.context.scene.atom_pdb[0] - dist = import_pdb.DEF_atom_pdb_distance() - - if dist != "N.A.": - # The string length is cut, 3 digits after the first 3 digits - # after the '.'. Append also "Angstrom". - # Remember: 1 Angstrom = 10^(-10) m - pos = str.find(dist, ".") - dist = dist[:pos+4] - dist = dist + " A" - - # Put the distance into the string of the output field. - scn.distance = dist - return {'FINISHED'} - - -# Button for increasing the radii of all atoms -class CLASS_atom_pdb_radius_all_bigger_button(Operator): - bl_idname = "atom_pdb.radius_all_bigger" - bl_label = "Bigger ..." - bl_description = "Increase the radii of the atoms" - - def execute(self, context): - scn = bpy.context.scene.atom_pdb[0] - import_pdb.DEF_atom_pdb_radius_all( - scn.radius_all, - scn.radius_how,) - return {'FINISHED'} - - -# Button for decreasing the radii of all atoms -class CLASS_atom_pdb_radius_all_smaller_button(Operator): - bl_idname = "atom_pdb.radius_all_smaller" - bl_label = "Smaller ..." - bl_description = "Decrease the radii of the atoms" - - def execute(self, context): - scn = bpy.context.scene.atom_pdb[0] - import_pdb.DEF_atom_pdb_radius_all( - 1.0/scn.radius_all, - scn.radius_how,) - return {'FINISHED'} - - -# 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" - bl_description = "Show only the sticks (atom radii = stick radii)" - - def execute(self, context): - global ATOM_PDB_ERROR - scn = bpy.context.scene.atom_pdb[0] - - result = import_pdb.DEF_atom_pdb_radius_sticks( - 0.01, - scn.radius_how,) - if result == False: - ATOM_PDB_ERROR = "No sticks => no changes" - bpy.ops.atom_pdb.error_dialog('INVOKE_DEFAULT') - - 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" - bl_description = "Load the structure again" - - def execute(self, context): - scn = context.scene.atom_pdb[0] - - atom_number = import_pdb.DEF_atom_pdb_main( - scn.use_mesh, - scn.mesh_azimuth, - scn.mesh_zenith, - scn.scale_ballradius, - scn.atomradius, - scn.scale_distances, - scn.use_sticks, - scn.use_sticks_color, - scn.use_sticks_smooth, - scn.use_sticks_bonds, - scn.sticks_unit_length, - scn.sticks_dist, - scn.sticks_sectors, - scn.sticks_radius, - scn.use_center, - scn.use_camera, - scn.use_lamp, - scn.datafile) - - scn.number_atoms = str(atom_number) + " atoms" - - return {'FINISHED'} - - -def DEF_panel_yes_no(): - global ATOM_PDB_PANEL - - datafile_path = bpy.utils.user_resource('SCRIPTS', path='', create=False) - if os.path.isdir(datafile_path) == False: - bpy.utils.user_resource('SCRIPTS', path='', create=True) - datafile_path = os.path.join(datafile_path, "presets") - if os.path.isdir(datafile_path) == False: - os.mkdir(datafile_path) - datafile = os.path.join(datafile_path, "io_mesh_pdb.pref") - if os.path.isfile(datafile): - datafile_fp = io.open(datafile, "r") - for line in datafile_fp: - if "Panel" in line: - ATOM_PDB_PANEL = line[-2:] - ATOM_PDB_PANEL = ATOM_PDB_PANEL[0:1] - bpy.context.scene.use_panel = ATOM_PDB_PANEL - break - datafile_fp.close() - else: - DEF_panel_write_pref("0") - - -def DEF_panel_write_pref(value): - datafile_path = bpy.utils.user_resource('SCRIPTS', path='', create=False) - datafile_path = os.path.join(datafile_path, "presets") - datafile = os.path.join(datafile_path, "io_mesh_pdb.pref") - datafile_fp = io.open(datafile, "w") - datafile_fp.write("Atomic Blender PDB - Import/Export - Preferences\n") - datafile_fp.write("================================================\n") - datafile_fp.write("\n") - datafile_fp.write("Panel: "+value+"\n\n\n") - datafile_fp.close() - - -class CLASS_atom_pdb_error_dialog(bpy.types.Operator): - bl_idname = "atom_pdb.error_dialog" - bl_label = "Attention !" - - def draw(self, context): - layout = self.layout - row = layout.row() - row.label(text=" "+ATOM_PDB_ERROR) - def execute(self, context): - print("Atomic Blender - Error: "+ATOM_PDB_ERROR+"\n") - return {'FINISHED'} - def invoke(self, context, event): - return context.window_manager.invoke_props_dialog(self) - # This is the class for the file dialog of the importer. class CLASS_ImportPDB(Operator, ImportHelper): @@ -566,13 +77,6 @@ class CLASS_ImportPDB(Operator, ImportHelper): filename_ext = ".pdb" filter_glob = StringProperty(default="*.pdb", options={'HIDDEN'},) - bpy.types.Scene.use_panel = EnumProperty( - name="Panel", - description="Choose whether the panel shall appear or not in the View 3D.", - items=(('0', "Once", "The panel appears only in this session"), - ('1', "Always", "The panel always appears when Blender is started"), - ('2', "Never", "The panel never appears")), - default='0') use_camera = BoolProperty( name="Camera", default=False, description="Do you need a camera?") @@ -673,12 +177,10 @@ class CLASS_ImportPDB(Operator, ImportHelper): col.prop(self, "sticks_dist") row = layout.row() row.prop(self, "use_center") - row = layout.row() - row.prop(bpy.context.scene, "use_panel") def execute(self, context): # This is in order to solve this strange 'relative path' thing. - import_pdb.ATOM_PDB_FILEPATH = bpy.path.abspath(self.filepath) + filepath_pdb = bpy.path.abspath(self.filepath) # Execute main routine atom_number = import_pdb.DEF_atom_pdb_main( @@ -699,35 +201,7 @@ class CLASS_ImportPDB(Operator, ImportHelper): self.use_center, self.use_camera, self.use_lamp, - self.datafile) - - # Copy the whole bunch of values into the property collection. - scn = context.scene.atom_pdb[0] - scn.use_mesh = self.use_mesh - scn.mesh_azimuth = self.mesh_azimuth - scn.mesh_zenith = self.mesh_zenith - scn.scale_ballradius = self.scale_ballradius - scn.atomradius = self.atomradius - scn.scale_distances = self.scale_distances - scn.use_sticks = self.use_sticks - scn.use_sticks_color = self.use_sticks_color - scn.use_sticks_smooth = self.use_sticks_smooth - scn.use_sticks_bonds = self.use_sticks_bonds - scn.sticks_unit_length = self.sticks_unit_length - scn.sticks_dist = self.sticks_dist - scn.sticks_sectors = self.sticks_sectors - scn.sticks_radius = self.sticks_radius - scn.use_center = self.use_center - scn.use_camera = self.use_camera - scn.use_lamp = self.use_lamp - scn.datafile = self.datafile - - scn.number_atoms = str(atom_number) + " atoms" - scn.PDB_file = import_pdb.ATOM_PDB_FILEPATH - - global ATOM_PDB_PANEL - ATOM_PDB_PANEL = bpy.context.scene.use_panel - DEF_panel_write_pref(bpy.context.scene.use_panel) + filepath_pdb) return {'FINISHED'} @@ -773,12 +247,9 @@ def DEF_menu_func_export(self, context): def register(): - DEF_panel_yes_no() bpy.utils.register_module(__name__) bpy.types.INFO_MT_file_import.append(DEF_menu_func_import) bpy.types.INFO_MT_file_export.append(DEF_menu_func_export) - bpy.types.Scene.atom_pdb = bpy.props.CollectionProperty(type=CLASS_atom_pdb_Properties) - bpy.context.scene.atom_pdb.add() def unregister(): bpy.utils.unregister_module(__name__) diff --git a/io_mesh_pdb/import_pdb.py b/io_mesh_pdb/import_pdb.py index 70795f6c1..f033b531d 100644 --- a/io_mesh_pdb/import_pdb.py +++ b/io_mesh_pdb/import_pdb.py @@ -25,17 +25,6 @@ from math import pi, cos, sin from mathutils import Vector, Matrix from copy import copy -# This variable contains the path of the PDB file. -# It is used almost everywhere, which explains why it -# should stay global. First, it is empty and gets 'filled' directly -# after having chosen the PDB file (see 'class LoadPDB' further below). - -ATOM_PDB_FILEPATH = "" - -# Some string stuff for the console. -ATOM_PDB_STRING = "Atomic Blender\n===================" - - # ----------------------------------------------------------------------------- # Atom, stick and element data @@ -200,393 +189,11 @@ class CLASS_atom_pdb_stick(object): self.number = number self.dist = dist -# ----------------------------------------------------------------------------- -# Some small routines - -# Routine which produces a cylinder. All is somewhat easy to undertsand. -def DEF_atom_pdb_build_stick(radius, length, sectors): - - dphi = 2.0 * pi/(float(sectors)-1) - - # Vertices - vertices_top = [Vector((0,0,length / 2.0))] - vertices_bottom = [Vector((0,0,-length / 2.0))] - vertices = [] - for i in range(sectors-1): - x = radius * cos( dphi * i ) - y = radius * sin( dphi * i ) - z = length / 2.0 - vertex = Vector((x,y,z)) - vertices_top.append(vertex) - z = -length / 2.0 - vertex = Vector((x,y,z)) - vertices_bottom.append(vertex) - vertices = vertices_top + vertices_bottom - - # Side facets (Cylinder) - faces1 = [] - for i in range(sectors-1): - if i == sectors-2: - faces1.append( [i+1, 1, 1+sectors, i+1+sectors] ) - else: - faces1.append( [i+1, i+2, i+2+sectors, i+1+sectors] ) - - # Top facets - faces2 = [] - for i in range(sectors-1): - if i == sectors-2: - face_top = [0,sectors-1,1] - face_bottom = [sectors,2*sectors-1,sectors+1] - else: - face_top = [0] - face_bottom = [sectors] - for j in range(2): - face_top.append(i+j+1) - face_bottom.append(i+j+1+sectors) - faces2.append(face_top) - faces2.append(face_bottom) - - # Build the mesh, Cylinder - cylinder = bpy.data.meshes.new("Sticks_Cylinder") - cylinder.from_pydata(vertices, [], faces1) - cylinder.update() - new_cylinder = bpy.data.objects.new("Sticks_Cylinder", cylinder) - bpy.context.scene.objects.link(new_cylinder) - - # Build the mesh, Cups - cups = bpy.data.meshes.new("Sticks_Cups") - cups.from_pydata(vertices, [], faces2) - cups.update() - new_cups = bpy.data.objects.new("Sticks_Cups", cups) - bpy.context.scene.objects.link(new_cups) - - return (new_cylinder, new_cups) - - -# This function measures the distance between two active objects (atoms). -def DEF_atom_pdb_distance(): - - if len(bpy.context.selected_bases) > 1: - object_1 = bpy.context.selected_objects[0] - object_2 = bpy.context.selected_objects[1] - else: - return "N.A." - - dv = object_2.location - object_1.location - return str(dv.length) - - -# 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): - - if how == "ALL_IN_LAYER": - - # Note all layers that are active. - layers = [] - for i in range(20): - if bpy.context.scene.layers[i] == True: - layers.append(i) - - # Put all objects, which are in the layers, into a list. - change_objects = [] - for obj in bpy.context.scene.objects: - for layer in layers: - if obj.layers[layer] == True: - change_objects.append(obj) - - # Consider all objects, which are in the list 'change_objects'. - for obj in change_objects: - if len(obj.children) != 0: - if obj.children[0].type == "SURFACE" or obj.children[0].type == "MESH": - for element in ATOM_PDB_ELEMENTS: - if element.name in obj.name: - obj.children[0].scale = (element.radii[int(rtype)],) * 3 - else: - if obj.type == "SURFACE" or obj.type == "MESH": - for element in ATOM_PDB_ELEMENTS: - if element.name in obj.name: - obj.scale = (element.radii[int(rtype)],) * 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": - for element in ATOM_PDB_ELEMENTS: - if element.name in obj.name: - obj.children[0].scale = (element.radii[int(rtype)],) * 3 - else: - if obj.type == "SURFACE" or obj.type == "MESH": - for element in ATOM_PDB_ELEMENTS: - if element.name in obj.name: - obj.scale = (element.radii[int(rtype)],) * 3 - - -# Routine to modify the radii in picometer of a specific type of atom -def DEF_atom_pdb_radius_pm(atomname, radius_pm, 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 atomname in obj.name: - 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: - 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: - 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: - if "Stick" not in obj.name: - obj.scale = (radius_pm/100,) * 3 - - -# Routine to scale the radii of all atoms -def DEF_atom_pdb_radius_all(scale, 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 *= scale - else: - if obj.type == "SURFACE" or obj.type == "MESH": - if "Stick" not in obj.name: - obj.scale *= scale - - 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 *= scale - else: - if obj.type == "SURFACE" or obj.type == "MESH": - if "Stick" not in obj.name: - 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): - - # Are there any sticks? - Found = False - 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" in obj.name: - Found = True - else: - if obj.type == "SURFACE" or obj.type == "MESH": - if "Stick" in obj.name: - Found = True - - 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" in obj.name: - Found = True - else: - if obj.type == "SURFACE" or obj.type == "MESH": - if "Stick" in obj.name: - Found = True - - if Found == False: - return False - - 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 - - return True - -# ----------------------------------------------------------------------------- -# The custom data file - -def DEF_atom_pdb_custom_datafile(path_datafile): - - if path_datafile == "": - return False - - path_datafile = bpy.path.abspath(path_datafile) - - if os.path.isfile(path_datafile) == False: - return False - - # The whole list gets deleted! We build it new. - ATOM_PDB_ELEMENTS[:] = [] - - # Read the data file, which contains all data - # (atom name, radii, colors, etc.) - data_file_p = io.open(path_datafile, "r") - - for line in data_file_p: - - if "Atom" in line: - - line = data_file_p.readline() - - # Number - line = data_file_p.readline() - number = line[19:-1] - # Name - line = data_file_p.readline() - name = line[19:-1] - # Short name - line = data_file_p.readline() - short_name = line[19:-1] - # Color - line = data_file_p.readline() - color_value = line[19:-1].split(',') - color = [float(color_value[0]), - float(color_value[1]), - float(color_value[2])] - # Used radius - line = data_file_p.readline() - radius_used = float(line[19:-1]) - # Atomic radius - line = data_file_p.readline() - radius_atomic = float(line[19:-1]) - # Van der Waals radius - line = data_file_p.readline() - radius_vdW = float(line[19:-1]) - - radii = [radius_used,radius_atomic,radius_vdW] - radii_ionic = [] - - element = CLASS_atom_pdb_Elements(number,name,short_name,color, - radii, radii_ionic) - - ATOM_PDB_ELEMENTS.append(element) - - data_file_p.close() - - for obj in bpy.context.selected_objects: - if len(obj.children) != 0: - child = obj.children[0] - if child.type == "SURFACE" or child.type == "MESH": - for element in ATOM_PDB_ELEMENTS: - if element.name in obj.name: - child.scale = (element.radii[0],) * 3 - child.active_material.diffuse_color = element.color - else: - if obj.type == "SURFACE" or obj.type == "MESH": - for element in ATOM_PDB_ELEMENTS: - if element.name in obj.name: - obj.scale = (element.radii[0],) * 3 - obj.active_material.diffuse_color = element.color - - return True # ----------------------------------------------------------------------------- -# The main routine - -def DEF_atom_pdb_main(use_mesh,Ball_azimuth,Ball_zenith, - Ball_radius_factor,radiustype,Ball_distance_factor, - use_sticks,use_sticks_color,use_sticks_smooth, - use_sticks_bonds, Stick_unit, 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. - all_atoms = [] - - # The list of all sticks. - all_sticks = [] - - # List of materials - atom_material_list = [] +# Some basic routines - # A list of ALL objects which are loaded (needed for selecting the loaded - # structure. - atom_object_list = [] - - - # ------------------------------------------------------------------------ - # INITIALIZE THE ELEMENT LIST +def DEF_atom_pdb_read_elements(): ATOM_PDB_ELEMENTS[:] = [] @@ -602,17 +209,21 @@ def DEF_atom_pdb_main(use_mesh,Ball_azimuth,Ball_zenith, radii,radii_ionic) ATOM_PDB_ELEMENTS.append(li) - # ------------------------------------------------------------------------ - # READING DATA OF ATOMS - if DEF_atom_pdb_custom_datafile(path_datafile): - print("Custom data file is loaded.") +# filepath_pdb: path to pdb file +# radiustype : '0' default +# '1' atomic radii +# '2' van der Waals +def DEF_atom_pdb_read_pdb_file(filepath_pdb,radiustype): + + # The list of all atoms as read from the PDB file. + all_atoms = [] - # Open the file ... - ATOM_PDB_FILEPATH_p = io.open(ATOM_PDB_FILEPATH, "r") + # Open the pdb file ... + filepath_pdb_p = io.open(filepath_pdb, "r") #Go to the line, in which "ATOM" or "HETATM" appears. - for line in ATOM_PDB_FILEPATH_p: + for line in filepath_pdb_p: split_list = line.split(' ') if "ATOM" in split_list[0]: break @@ -729,80 +340,31 @@ def DEF_atom_pdb_main(use_mesh,Ball_azimuth,Ball_zenith, radius, color,[])) - line = ATOM_PDB_FILEPATH_p.readline() + line = filepath_pdb_p.readline() line = line[:-1] - ATOM_PDB_FILEPATH_p.close() + filepath_pdb_p.close() # From above it can be clearly seen that j is now the number of all atoms. Number_of_total_atoms = j - # ------------------------------------------------------------------------ - # MATERIAL PROPERTIES FOR ATOMS - - # The list that contains info about all types of atoms is created - # here. It is used for building the material properties for - # instance (see below). - atom_all_types_list = [] - - for atom in all_atoms: - FLAG_FOUND = False - for atom_type in atom_all_types_list: - # If the atom name is already in the list, FLAG on 'True'. - if atom_type[0] == atom.name: - FLAG_FOUND = True - break - # No name in the current list has been found? => New entry. - if FLAG_FOUND == False: - # Stored are: Atom label (e.g. 'Na'), the corresponding atom - # name (e.g. 'Sodium') and its color. - atom_all_types_list.append([atom.name, atom.element, atom.color]) - - # The list of materials is built. - # Note that all atoms of one type (e.g. all hydrogens) get only ONE - # material! This is good because then, by activating one atom in the - # Blender scene and changing the color of this atom, one changes the color - # of ALL atoms of the same type at the same time. - - # Create first a new list of materials for each type of atom - # (e.g. hydrogen) - for atom_type in atom_all_types_list: - material = bpy.data.materials.new(atom_type[1]) - material.name = atom_type[0] - material.diffuse_color = atom_type[2] - atom_material_list.append(material) + return (Number_of_total_atoms, all_atoms) + - # Now, we go through all atoms and give them a material. For all atoms ... - for atom in all_atoms: - # ... and all materials ... - for material in atom_material_list: - # ... select the correct material for the current atom via - # comparison of names ... - if atom.name in material.name: - # ... and give the atom its material properties. - # However, before we check, if it is a vacancy, because then it - # gets some additional preparation. The vacancy is represented - # by a transparent cube. - if atom.name == "Vacancy": - material.transparency_method = 'Z_TRANSPARENCY' - material.alpha = 1.3 - material.raytrace_transparency.fresnel = 1.6 - material.raytrace_transparency.fresnel_factor = 1.6 - material.use_transparency = True - # The atom gets its properties. - atom.material = material +def DEF_atom_pdb_read_pdb_file_sticks(filepath_pdb, + use_sticks_bonds): - # ------------------------------------------------------------------------ - # READING DATA OF STICKS + # The list of all sticks. + all_sticks = [] - # Open the PDB file again such that the file pointer is in the first - # line ... . Stupid, I know ... ;-) - ATOM_PDB_FILEPATH_p = io.open(ATOM_PDB_FILEPATH, "r") + # Open the PDB file again. + filepath_pdb_p = io.open(filepath_pdb, "r") + line = filepath_pdb_p.readline() split_list = line.split(' ') # Go to the first entry if "CONECT" not in split_list[0]: - for line in ATOM_PDB_FILEPATH_p: + for line in filepath_pdb_p: split_list = line.split(' ') if "CONECT" in split_list[0]: break @@ -902,11 +464,178 @@ def DEF_atom_pdb_main(use_mesh,Ball_azimuth,Ball_zenith, Number_of_sticks += 1 j += 1 - line = ATOM_PDB_FILEPATH_p.readline() + line = filepath_pdb_p.readline() line = line.rstrip() - ATOM_PDB_FILEPATH_p.close() - # So far, all atoms and sticks have been registered. + filepath_pdb_p.close() + + return all_sticks + + +# Routine which produces a cylinder. All is somewhat easy to undertsand. +def DEF_atom_pdb_build_stick(radius, length, sectors): + + dphi = 2.0 * pi/(float(sectors)-1) + + # Vertices + vertices_top = [Vector((0,0,length / 2.0))] + vertices_bottom = [Vector((0,0,-length / 2.0))] + vertices = [] + for i in range(sectors-1): + x = radius * cos( dphi * i ) + y = radius * sin( dphi * i ) + z = length / 2.0 + vertex = Vector((x,y,z)) + vertices_top.append(vertex) + z = -length / 2.0 + vertex = Vector((x,y,z)) + vertices_bottom.append(vertex) + vertices = vertices_top + vertices_bottom + + # Side facets (Cylinder) + faces1 = [] + for i in range(sectors-1): + if i == sectors-2: + faces1.append( [i+1, 1, 1+sectors, i+1+sectors] ) + else: + faces1.append( [i+1, i+2, i+2+sectors, i+1+sectors] ) + + # Top facets + faces2 = [] + for i in range(sectors-1): + if i == sectors-2: + face_top = [0,sectors-1,1] + face_bottom = [sectors,2*sectors-1,sectors+1] + else: + face_top = [0] + face_bottom = [sectors] + for j in range(2): + face_top.append(i+j+1) + face_bottom.append(i+j+1+sectors) + faces2.append(face_top) + faces2.append(face_bottom) + + # Build the mesh, Cylinder + cylinder = bpy.data.meshes.new("Sticks_Cylinder") + cylinder.from_pydata(vertices, [], faces1) + cylinder.update() + new_cylinder = bpy.data.objects.new("Sticks_Cylinder", cylinder) + bpy.context.scene.objects.link(new_cylinder) + + # Build the mesh, Cups + cups = bpy.data.meshes.new("Sticks_Cups") + cups.from_pydata(vertices, [], faces2) + cups.update() + new_cups = bpy.data.objects.new("Sticks_Cups", cups) + bpy.context.scene.objects.link(new_cups) + + return (new_cylinder, new_cups) + + +# ----------------------------------------------------------------------------- +# The main routine + +def DEF_atom_pdb_main(use_mesh, + Ball_azimuth, + Ball_zenith, + Ball_radius_factor, + radiustype, + Ball_distance_factor, + use_sticks, + use_sticks_color, + use_sticks_smooth, + use_sticks_bonds, + Stick_unit, Stick_dist, + Stick_sectors, + Stick_diameter, + put_to_center, + use_camera, + use_lamp, + filepath_pdb): + + + # List of materials + atom_material_list = [] + + # A list of ALL objects which are loaded (needed for selecting the loaded + # structure. + atom_object_list = [] + + + # ------------------------------------------------------------------------ + # INITIALIZE THE ELEMENT LIST + + DEF_atom_pdb_read_elements() + + # ------------------------------------------------------------------------ + # READING DATA OF ATOMS + + (Number_of_total_atoms, all_atoms) = DEF_atom_pdb_read_pdb_file(filepath_pdb, + radiustype) + + # ------------------------------------------------------------------------ + # MATERIAL PROPERTIES FOR ATOMS + + # The list that contains info about all types of atoms is created + # here. It is used for building the material properties for + # instance (see below). + atom_all_types_list = [] + + for atom in all_atoms: + FLAG_FOUND = False + for atom_type in atom_all_types_list: + # If the atom name is already in the list, FLAG on 'True'. + if atom_type[0] == atom.name: + FLAG_FOUND = True + break + # No name in the current list has been found? => New entry. + if FLAG_FOUND == False: + # Stored are: Atom label (e.g. 'Na'), the corresponding atom + # name (e.g. 'Sodium') and its color. + atom_all_types_list.append([atom.name, atom.element, atom.color]) + + # The list of materials is built. + # Note that all atoms of one type (e.g. all hydrogens) get only ONE + # material! This is good because then, by activating one atom in the + # Blender scene and changing the color of this atom, one changes the color + # of ALL atoms of the same type at the same time. + + # Create first a new list of materials for each type of atom + # (e.g. hydrogen) + for atom_type in atom_all_types_list: + material = bpy.data.materials.new(atom_type[1]) + material.name = atom_type[0] + material.diffuse_color = atom_type[2] + atom_material_list.append(material) + + # Now, we go through all atoms and give them a material. For all atoms ... + for atom in all_atoms: + # ... and all materials ... + for material in atom_material_list: + # ... select the correct material for the current atom via + # comparison of names ... + if atom.name in material.name: + # ... and give the atom its material properties. + # However, before we check, if it is a vacancy, because then it + # gets some additional preparation. The vacancy is represented + # by a transparent cube. + if atom.name == "Vacancy": + material.transparency_method = 'Z_TRANSPARENCY' + material.alpha = 1.3 + material.raytrace_transparency.fresnel = 1.6 + material.raytrace_transparency.fresnel_factor = 1.6 + material.use_transparency = True + # The atom gets its properties. + atom.material = material + + # ------------------------------------------------------------------------ + # READING DATA OF STICKS + + all_sticks = DEF_atom_pdb_read_pdb_file_sticks(filepath_pdb, + use_sticks_bonds) + + + # So far, all atoms, sticks and materials have been registered. # ------------------------------------------------------------------------ # TRANSLATION OF THE STRUCTURE TO THE ORIGIN @@ -975,16 +704,14 @@ def DEF_atom_pdb_main(use_mesh,Ball_azimuth,Ball_zenith, camera_xyz_vec = object_center_vec + object_camera_vec # Create the camera - current_layers=bpy.context.scene.layers - bpy.ops.object.camera_add(view_align=False, enter_editmode=False, - location=camera_xyz_vec, - rotation=(0.0, 0.0, 0.0), layers=current_layers) - # Some properties of the camera are changed. - camera = bpy.context.scene.objects.active - camera.name = "A_camera" - camera.data.name = "A_camera" - camera.data.lens = 45 - camera.data.clip_end = 500.0 + current_layers=bpy.context.scene.layers + camera_data = bpy.data.cameras.new("A_camera") + camera_data.lens = 45 + camera_data.clip_end = 500.0 + camera = bpy.data.objects.new("A_camera", camera_data) + camera.location = camera_xyz_vec + camera.layers = current_layers + bpy.context.scene.objects.link(camera) # Here the camera is rotated such it looks towards the center of # the object. The [0.0, 0.0, 1.0] vector along the z axis @@ -1027,35 +754,18 @@ def DEF_atom_pdb_main(use_mesh,Ball_azimuth,Ball_zenith, # Create the lamp current_layers=bpy.context.scene.layers - bpy.ops.object.lamp_add (type = 'POINT', view_align=False, - location=lamp_xyz_vec, - rotation=(0.0, 0.0, 0.0), - layers=current_layers) - # Some properties of the lamp are changed. - lamp = bpy.context.scene.objects.active - lamp.data.name = "A_lamp" - lamp.name = "A_lamp" - lamp.data.distance = 500.0 - lamp.data.energy = 3.0 - lamp.data.shadow_method = 'RAY_SHADOW' + lamp_data = bpy.data.lamps.new(name="A_lamp", type="POINT") + lamp_data.distance = 500.0 + lamp_data.energy = 3.0 + lamp_data.shadow_method = 'RAY_SHADOW' + lamp = bpy.data.objects.new("A_lamp", lamp_data) + lamp.location = lamp_xyz_vec + lamp.layers = current_layers + bpy.context.scene.objects.link(lamp) bpy.context.scene.world.light_settings.use_ambient_occlusion = True bpy.context.scene.world.light_settings.ao_factor = 0.2 - # ------------------------------------------------------------------------ - # SOME OUTPUT ON THE CONSOLE - - print() - print() - print() - print(ATOM_PDB_STRING) - print() - print("Total number of atoms : " + str(Number_of_total_atoms)) - print("Total number of sticks : " + str(Number_of_sticks)) - print("Center of object (Angstrom) : ", object_center_vec) - print("Size of object (Angstrom) : ", object_size) - print() - # ------------------------------------------------------------------------ # SORTING THE ATOMS @@ -1154,8 +864,6 @@ def DEF_atom_pdb_main(use_mesh,Ball_azimuth,Ball_zenith, new_atom_mesh.location = object_center_vec atom_object_list.append(new_atom_mesh) - print() - # ------------------------------------------------------------------------ # DRAWING THE STICKS @@ -1323,7 +1031,3 @@ def DEF_atom_pdb_main(use_mesh,Ball_azimuth,Ball_zenith, if obj: bpy.context.scene.objects.active = obj - print("\n\nAll atoms (%d) and sticks (%d) have been drawn - finished.\n\n" - % (Number_of_total_atoms,Number_of_sticks)) - - return Number_of_total_atoms -- GitLab