From e333e19bed04fce92eb60fc4ca16d501c754c16b Mon Sep 17 00:00:00 2001 From: Clemens Barth <barth@root-1.de> Date: Fri, 22 Mar 2013 17:09:36 +0000 Subject: [PATCH] New feature: Somebody asked me to make use of the skin and subdivision modifier in Blender, which both can be used to draw the sticks. This is what I have done and implemented. The code has become a bit 'unreadable' there will be after some other commitments including some code cleanups. Blendphys. --- io_mesh_pdb/__init__.py | 57 ++++++++++---- io_mesh_pdb/import_pdb.py | 155 +++++++++++++++++++++++++++++++++++++- 2 files changed, 193 insertions(+), 19 deletions(-) diff --git a/io_mesh_pdb/__init__.py b/io_mesh_pdb/__init__.py index be787ca1e..19c7c4a47 100644 --- a/io_mesh_pdb/__init__.py +++ b/io_mesh_pdb/__init__.py @@ -24,7 +24,7 @@ # # Start of project : 2011-08-31 by Clemens Barth # First publication in Blender : 2011-11-11 -# Last modified : 2013-01-16 +# Last modified : 2013-03-22 # # Acknowledgements # ================ @@ -38,7 +38,7 @@ bl_info = { "name": "Atomic Blender - PDB", "description": "Loading and manipulating atoms from PDB files", "author": "Clemens Barth", - "version": (1, 5), + "version": (1, 6), "blender": (2, 60, 0), "location": "File -> Import -> PDB (.pdb)", "warning": "", @@ -107,12 +107,21 @@ class ImportPDB(Operator, ImportHelper): default='0',) use_sticks = BoolProperty( name="Use sticks", default=True, - description="Do you want to display the sticks?") + description="Do you want to display the sticks?") + use_sticks_skin = BoolProperty( + name="Use skin modifier", default=False, + description="Do you want to display the sticks with the skin modifier?") + sticks_subdiv_view = IntProperty( + name = "SubDivV", default=2, min=1, + description="Number of subdivisions (view)") + sticks_subdiv_render = IntProperty( + name = "SubDivR", default=2, min=1, + description="Number of subdivisions (render)") 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, + name = "Radius", default=0.2, min=0.0001, description ="Radius of a stick") sticks_unit_length = FloatProperty( name = "Unit", default=0.05, min=0.0001, @@ -142,6 +151,8 @@ class ImportPDB(Operator, ImportHelper): row.prop(self, "use_camera") row.prop(self, "use_lamp") row = layout.row() + row.prop(self, "use_center") + row = layout.row() col = row.column() col.prop(self, "ball") row = layout.row() @@ -158,26 +169,39 @@ class ImportPDB(Operator, ImportHelper): row = layout.row() row.prop(self, "atomradius") row = layout.row() - col = row.column() - col.prop(self, "use_sticks") + # Sticks + row.prop(self, "use_sticks") + row = layout.row() + row.active = self.use_sticks + row.prop(self, "use_sticks_skin") row = layout.row() row.active = self.use_sticks col = row.column() - col.prop(self, "sticks_sectors") + if not self.use_sticks_skin: + col.prop(self, "sticks_sectors") col.prop(self, "sticks_radius") - col.prop(self, "sticks_unit_length") - col = row.column(align=True) - col.prop(self, "use_sticks_color") + if self.use_sticks_skin: + row = layout.row() + row.active = self.use_sticks + row.prop(self, "sticks_subdiv_view") + row.prop(self, "sticks_subdiv_render") + row = layout.row() + row.active = self.use_sticks + if not self.use_sticks_skin: + col.prop(self, "sticks_unit_length") + col = row.column(align=True) + if not self.use_sticks_skin: + col.prop(self, "use_sticks_color") col.prop(self, "use_sticks_smooth") - col.prop(self, "use_sticks_bonds") + if not self.use_sticks_skin: + col.prop(self, "use_sticks_bonds") row = layout.row() row.active = self.use_sticks col = row.column(align=True) col = row.column(align=True) - col.active = self.use_sticks and self.use_sticks_bonds - col.prop(self, "sticks_dist") - row = layout.row() - row.prop(self, "use_center") + col.active = self.use_sticks + if not self.use_sticks_skin: + col.prop(self, "sticks_dist") def execute(self, context): # This is in order to solve this strange 'relative path' thing. @@ -192,6 +216,9 @@ class ImportPDB(Operator, ImportHelper): self.atomradius, self.scale_distances, self.use_sticks, + self.use_sticks_skin, + self.sticks_subdiv_view, + self.sticks_subdiv_render, self.use_sticks_color, self.use_sticks_smooth, self.use_sticks_bonds, diff --git a/io_mesh_pdb/import_pdb.py b/io_mesh_pdb/import_pdb.py index 546398057..f669b3a21 100644 --- a/io_mesh_pdb/import_pdb.py +++ b/io_mesh_pdb/import_pdb.py @@ -17,6 +17,7 @@ # ##### END GPL LICENSE BLOCK ##### import bpy +import bmesh from math import pi, cos, sin, sqrt, ceil from mathutils import Vector, Matrix from copy import copy @@ -188,6 +189,8 @@ class StickProp(object): # ----------------------------------------------------------------------------- # Some basic routines + +# The function, which reads all necessary properties of the elements. def read_elements(): del ELEMENTS[:] @@ -205,6 +208,9 @@ def read_elements(): ELEMENTS.append(li) +# The function, which reads the x,y,z positions of all atoms in a PDB +# file. +# # filepath_pdb: path to pdb file # radiustype : '0' default # '1' atomic radii @@ -343,14 +349,15 @@ def read_pdb_file(filepath_pdb, radiustype): Number_of_total_atoms = j return (Number_of_total_atoms, all_atoms) - + +# The function, which reads the sticks in a PDB file. def read_pdb_file_sticks(filepath_pdb, use_sticks_bonds, all_atoms): # The list of all sticks. all_sticks = [] - # Open the PDB file again. + # Open the PDB file. filepath_pdb_p = open(filepath_pdb, "r") line = filepath_pdb_p.readline() @@ -411,6 +418,7 @@ def read_pdb_file_sticks(filepath_pdb, use_sticks_bonds, all_atoms): if use_sticks_bonds == True: number = atom_list[1:].count(atom2) + if number == 2 or number == 3: basis_list = list(set(atom_list[1:])) @@ -536,6 +544,9 @@ def import_pdb(Ball_type, radiustype, Ball_distance_factor, use_sticks, + use_sticks_skin, + sticks_subdiv_view, + sticks_subdiv_render, use_sticks_color, use_sticks_smooth, use_sticks_bonds, @@ -863,9 +874,145 @@ def import_pdb(Ball_type, atom_object_list.append(new_atom_mesh) # ------------------------------------------------------------------------ - # DRAWING THE STICKS + # DRAWING THE STICKS: skin and subdivision modifier + + if use_sticks == True and use_sticks_skin == True and all_sticks != []: + + # These counters are for the edges, in the shape [i,i+1]. + i = 0 + + # This is the list of vertices, containing the atom position + # (vectors)). + stick_vertices = [] + # This is the 'same' list, which contains not vector position of + # the atoms but their numbers. It is used to handle the edges. + stick_vertices_nr = [] + # This is the list of edges. + stick_edges = [] + + # Go through the list of all sticks. For each stick do: + for stick in all_sticks: + + # Each stick has two atoms = two vertices. + + """ + [ 0,1 , 3,4 , 0,8 , 7,3] + [[0,1], [2,3], [4,5], [6,7]] + + [ 0,1 , 3,4 , x,8 , 7,x] x:deleted + [[0,1], [2,3], [0,5], [6,2]] + """ + + # Check, if the vertex (atom) is already in the vertex list. + # edge: [s1,s2] + FLAG_s1 = False + s1 = 0 + for stick2 in stick_vertices_nr: + if stick2 == stick.atom1-1: + FLAG_s1 = True + break + s1 += 1 + FLAG_s2 = False + s2 = 0 + for stick2 in stick_vertices_nr: + if stick2 == stick.atom2-1: + FLAG_s2 = True + break + s2 += 1 + + # If the vertex (atom) is not yet in the vertex list: + # append the number of atom and the vertex to the two lists. + # For the first atom: + if FLAG_s1 == False: + atom1 = copy(all_atoms[stick.atom1-1].location) + stick_vertices.append(atom1) + stick_vertices_nr.append(stick.atom1-1) + # For the second atom: + if FLAG_s2 == False: + atom2 = copy(all_atoms[stick.atom2-1].location) + stick_vertices.append(atom2) + stick_vertices_nr.append(stick.atom2-1) + + # Build the edges: + + # If both vertices (atoms) were not in the lists, then + # the edge is simply [i,i+1]. These are two new vertices + # (atoms), so increase i by 2. + if FLAG_s1 == False and FLAG_s2 == False: + stick_edges.append([i,i+1]) + i += 2 + # Both vertices (atoms) were already in the list, so then + # use the vertices (atoms), which already exist. They are + # at positions s1 and s2. + if FLAG_s1 == True and FLAG_s2 == True: + stick_edges.append([s1,s2]) + # The following two if cases describe the situation that + # only one vertex (atom) was in the list. Since only ONE + # new vertex was added, increase i by one. + if FLAG_s1 == True and FLAG_s2 == False: + stick_edges.append([s1,i]) + i += 1 + if FLAG_s1 == False and FLAG_s2 == True: + stick_edges.append([i,s2]) + i += 1 + + # Build the mesh of the sticks + stick_mesh = bpy.data.meshes.new("Mesh_sticks") + stick_mesh.from_pydata(stick_vertices, stick_edges, []) + stick_mesh.update() + new_stick_mesh = bpy.data.objects.new("Sticks", stick_mesh) + bpy.context.scene.objects.link(new_stick_mesh) + + # Apply the skin modifier. + new_stick_mesh.modifiers.new(name="Sticks_skin", type='SKIN') + # Smooth the skin surface if this option has been chosen. + new_stick_mesh.modifiers[0].use_smooth_shade = use_sticks_smooth + # Apply the Subdivision modifier. + new_stick_mesh.modifiers.new(name="Sticks_subsurf", type='SUBSURF') + # Options: choose the levels + new_stick_mesh.modifiers[1].levels = sticks_subdiv_view + new_stick_mesh.modifiers[1].render_levels = sticks_subdiv_render + + # This is for putting the radiu of the sticks onto + # the desired value 'Stick_diameter' + bpy.context.scene.objects.active = new_stick_mesh + # EDIT mode + bpy.ops.object.mode_set(mode='EDIT', toggle=False) + bm = bmesh.from_edit_mesh(new_stick_mesh.data) + bpy.ops.mesh.select_all(action='DESELECT') + + # Select all vertices + for v in bm.verts: + v.select = True + + # This is somewhat a factor for the radius. + r_f = 4.0 + # Apply operator 'skin_resize'. + bpy.ops.transform.skin_resize(value=(Stick_diameter*r_f, + Stick_diameter*r_f, + Stick_diameter*r_f), + constraint_axis=(False, False, False), + constraint_orientation='GLOBAL', + mirror=False, + proportional='DISABLED', + proportional_edit_falloff='SMOOTH', + proportional_size=1, + snap=False, + snap_target='CLOSEST', + snap_point=(0, 0, 0), + snap_align=False, + snap_normal=(0, 0, 0), + texture_space=False, + release_confirm=False) + # Back to the OBJECT mode. + bpy.ops.object.mode_set(mode='OBJECT', toggle=False) + + + + # ------------------------------------------------------------------------ + # DRAWING THE STICKS: regular cylinders in a dupliverts structure - if use_sticks == True and all_sticks != []: + if use_sticks == True and all_sticks != [] and use_sticks_skin == False: dl = Stick_unit -- GitLab