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