1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
21 from math
import pi
, cos
, sin
, sqrt
, ceil
22 from mathutils
import Vector
, Matrix
25 # -----------------------------------------------------------------------------
26 # Atom, stick and element data
29 # This is a list that contains some data of all possible elements. The structure
32 # 1, "Hydrogen", "H", [0.0,0.0,1.0], 0.32, 0.32, 0.32 , -1 , 1.54 means
34 # No., name, short name, color, radius (used), radius (covalent), radius (atomic),
36 # charge state 1, radius (ionic) 1, charge state 2, radius (ionic) 2, ... all
37 # charge states for any atom are listed, if existing.
38 # The list is fixed and cannot be changed ... (see below)
41 ( 1, "Hydrogen", "H", ( 1.0, 1.0, 1.0), 0.32, 0.32, 0.79 , -1 , 1.54 ),
42 ( 2, "Helium", "He", ( 0.85, 1.0, 1.0), 0.93, 0.93, 0.49 ),
43 ( 3, "Lithium", "Li", ( 0.8, 0.50, 1.0), 1.23, 1.23, 2.05 , 1 , 0.68 ),
44 ( 4, "Beryllium", "Be", ( 0.76, 1.0, 0.0), 0.90, 0.90, 1.40 , 1 , 0.44 , 2 , 0.35 ),
45 ( 5, "Boron", "B", ( 1.0, 0.70, 0.70), 0.82, 0.82, 1.17 , 1 , 0.35 , 3 , 0.23 ),
46 ( 6, "Carbon", "C", ( 0.56, 0.56, 0.56), 0.77, 0.77, 0.91 , -4 , 2.60 , 4 , 0.16 ),
47 ( 7, "Nitrogen", "N", ( 0.18, 0.31, 0.97), 0.75, 0.75, 0.75 , -3 , 1.71 , 1 , 0.25 , 3 , 0.16 , 5 , 0.13 ),
48 ( 8, "Oxygen", "O", ( 1.0, 0.05, 0.05), 0.73, 0.73, 0.65 , -2 , 1.32 , -1 , 1.76 , 1 , 0.22 , 6 , 0.09 ),
49 ( 9, "Fluorine", "F", ( 0.56, 0.87, 0.31), 0.72, 0.72, 0.57 , -1 , 1.33 , 7 , 0.08 ),
50 (10, "Neon", "Ne", ( 0.70, 0.89, 0.96), 0.71, 0.71, 0.51 , 1 , 1.12 ),
51 (11, "Sodium", "Na", ( 0.67, 0.36, 0.94), 1.54, 1.54, 2.23 , 1 , 0.97 ),
52 (12, "Magnesium", "Mg", ( 0.54, 1.0, 0.0), 1.36, 1.36, 1.72 , 1 , 0.82 , 2 , 0.66 ),
53 (13, "Aluminium", "Al", ( 0.74, 0.65, 0.65), 1.18, 1.18, 1.82 , 3 , 0.51 ),
54 (14, "Silicon", "Si", ( 0.94, 0.78, 0.62), 1.11, 1.11, 1.46 , -4 , 2.71 , -1 , 3.84 , 1 , 0.65 , 4 , 0.42 ),
55 (15, "Phosphorus", "P", ( 1.0, 0.50, 0.0), 1.06, 1.06, 1.23 , -3 , 2.12 , 3 , 0.44 , 5 , 0.35 ),
56 (16, "Sulfur", "S", ( 1.0, 1.0, 0.18), 1.02, 1.02, 1.09 , -2 , 1.84 , 2 , 2.19 , 4 , 0.37 , 6 , 0.30 ),
57 (17, "Chlorine", "Cl", ( 0.12, 0.94, 0.12), 0.99, 0.99, 0.97 , -1 , 1.81 , 5 , 0.34 , 7 , 0.27 ),
58 (18, "Argon", "Ar", ( 0.50, 0.81, 0.89), 0.98, 0.98, 0.88 , 1 , 1.54 ),
59 (19, "Potassium", "K", ( 0.56, 0.25, 0.83), 2.03, 2.03, 2.77 , 1 , 0.81 ),
60 (20, "Calcium", "Ca", ( 0.23, 1.0, 0.0), 1.74, 1.74, 2.23 , 1 , 1.18 , 2 , 0.99 ),
61 (21, "Scandium", "Sc", ( 0.90, 0.90, 0.90), 1.44, 1.44, 2.09 , 3 , 0.73 ),
62 (22, "Titanium", "Ti", ( 0.74, 0.76, 0.78), 1.32, 1.32, 2.00 , 1 , 0.96 , 2 , 0.94 , 3 , 0.76 , 4 , 0.68 ),
63 (23, "Vanadium", "V", ( 0.65, 0.65, 0.67), 1.22, 1.22, 1.92 , 2 , 0.88 , 3 , 0.74 , 4 , 0.63 , 5 , 0.59 ),
64 (24, "Chromium", "Cr", ( 0.54, 0.6, 0.78), 1.18, 1.18, 1.85 , 1 , 0.81 , 2 , 0.89 , 3 , 0.63 , 6 , 0.52 ),
65 (25, "Manganese", "Mn", ( 0.61, 0.47, 0.78), 1.17, 1.17, 1.79 , 2 , 0.80 , 3 , 0.66 , 4 , 0.60 , 7 , 0.46 ),
66 (26, "Iron", "Fe", ( 0.87, 0.4, 0.2), 1.17, 1.17, 1.72 , 2 , 0.74 , 3 , 0.64 ),
67 (27, "Cobalt", "Co", ( 0.94, 0.56, 0.62), 1.16, 1.16, 1.67 , 2 , 0.72 , 3 , 0.63 ),
68 (28, "Nickel", "Ni", ( 0.31, 0.81, 0.31), 1.15, 1.15, 1.62 , 2 , 0.69 ),
69 (29, "Copper", "Cu", ( 0.78, 0.50, 0.2), 1.17, 1.17, 1.57 , 1 , 0.96 , 2 , 0.72 ),
70 (30, "Zinc", "Zn", ( 0.49, 0.50, 0.69), 1.25, 1.25, 1.53 , 1 , 0.88 , 2 , 0.74 ),
71 (31, "Gallium", "Ga", ( 0.76, 0.56, 0.56), 1.26, 1.26, 1.81 , 1 , 0.81 , 3 , 0.62 ),
72 (32, "Germanium", "Ge", ( 0.4, 0.56, 0.56), 1.22, 1.22, 1.52 , -4 , 2.72 , 2 , 0.73 , 4 , 0.53 ),
73 (33, "Arsenic", "As", ( 0.74, 0.50, 0.89), 1.20, 1.20, 1.33 , -3 , 2.22 , 3 , 0.58 , 5 , 0.46 ),
74 (34, "Selenium", "Se", ( 1.0, 0.63, 0.0), 1.16, 1.16, 1.22 , -2 , 1.91 , -1 , 2.32 , 1 , 0.66 , 4 , 0.50 , 6 , 0.42 ),
75 (35, "Bromine", "Br", ( 0.65, 0.16, 0.16), 1.14, 1.14, 1.12 , -1 , 1.96 , 5 , 0.47 , 7 , 0.39 ),
76 (36, "Krypton", "Kr", ( 0.36, 0.72, 0.81), 1.31, 1.31, 1.24 ),
77 (37, "Rubidium", "Rb", ( 0.43, 0.18, 0.69), 2.16, 2.16, 2.98 , 1 , 1.47 ),
78 (38, "Strontium", "Sr", ( 0.0, 1.0, 0.0), 1.91, 1.91, 2.45 , 2 , 1.12 ),
79 (39, "Yttrium", "Y", ( 0.58, 1.0, 1.0), 1.62, 1.62, 2.27 , 3 , 0.89 ),
80 (40, "Zirconium", "Zr", ( 0.58, 0.87, 0.87), 1.45, 1.45, 2.16 , 1 , 1.09 , 4 , 0.79 ),
81 (41, "Niobium", "Nb", ( 0.45, 0.76, 0.78), 1.34, 1.34, 2.08 , 1 , 1.00 , 4 , 0.74 , 5 , 0.69 ),
82 (42, "Molybdenum", "Mo", ( 0.32, 0.70, 0.70), 1.30, 1.30, 2.01 , 1 , 0.93 , 4 , 0.70 , 6 , 0.62 ),
83 (43, "Technetium", "Tc", ( 0.23, 0.61, 0.61), 1.27, 1.27, 1.95 , 7 , 0.97 ),
84 (44, "Ruthenium", "Ru", ( 0.14, 0.56, 0.56), 1.25, 1.25, 1.89 , 4 , 0.67 ),
85 (45, "Rhodium", "Rh", ( 0.03, 0.49, 0.54), 1.25, 1.25, 1.83 , 3 , 0.68 ),
86 (46, "Palladium", "Pd", ( 0.0, 0.41, 0.52), 1.28, 1.28, 1.79 , 2 , 0.80 , 4 , 0.65 ),
87 (47, "Silver", "Ag", ( 0.75, 0.75, 0.75), 1.34, 1.34, 1.75 , 1 , 1.26 , 2 , 0.89 ),
88 (48, "Cadmium", "Cd", ( 1.0, 0.85, 0.56), 1.48, 1.48, 1.71 , 1 , 1.14 , 2 , 0.97 ),
89 (49, "Indium", "In", ( 0.65, 0.45, 0.45), 1.44, 1.44, 2.00 , 3 , 0.81 ),
90 (50, "Tin", "Sn", ( 0.4, 0.50, 0.50), 1.41, 1.41, 1.72 , -4 , 2.94 , -1 , 3.70 , 2 , 0.93 , 4 , 0.71 ),
91 (51, "Antimony", "Sb", ( 0.61, 0.38, 0.70), 1.40, 1.40, 1.53 , -3 , 2.45 , 3 , 0.76 , 5 , 0.62 ),
92 (52, "Tellurium", "Te", ( 0.83, 0.47, 0.0), 1.36, 1.36, 1.42 , -2 , 2.11 , -1 , 2.50 , 1 , 0.82 , 4 , 0.70 , 6 , 0.56 ),
93 (53, "Iodine", "I", ( 0.58, 0.0, 0.58), 1.33, 1.33, 1.32 , -1 , 2.20 , 5 , 0.62 , 7 , 0.50 ),
94 (54, "Xenon", "Xe", ( 0.25, 0.61, 0.69), 1.31, 1.31, 1.24 ),
95 (55, "Caesium", "Cs", ( 0.34, 0.09, 0.56), 2.35, 2.35, 3.35 , 1 , 1.67 ),
96 (56, "Barium", "Ba", ( 0.0, 0.78, 0.0), 1.98, 1.98, 2.78 , 1 , 1.53 , 2 , 1.34 ),
97 (57, "Lanthanum", "La", ( 0.43, 0.83, 1.0), 1.69, 1.69, 2.74 , 1 , 1.39 , 3 , 1.06 ),
98 (58, "Cerium", "Ce", ( 1.0, 1.0, 0.78), 1.65, 1.65, 2.70 , 1 , 1.27 , 3 , 1.03 , 4 , 0.92 ),
99 (59, "Praseodymium", "Pr", ( 0.85, 1.0, 0.78), 1.65, 1.65, 2.67 , 3 , 1.01 , 4 , 0.90 ),
100 (60, "Neodymium", "Nd", ( 0.78, 1.0, 0.78), 1.64, 1.64, 2.64 , 3 , 0.99 ),
101 (61, "Promethium", "Pm", ( 0.63, 1.0, 0.78), 1.63, 1.63, 2.62 , 3 , 0.97 ),
102 (62, "Samarium", "Sm", ( 0.56, 1.0, 0.78), 1.62, 1.62, 2.59 , 3 , 0.96 ),
103 (63, "Europium", "Eu", ( 0.38, 1.0, 0.78), 1.85, 1.85, 2.56 , 2 , 1.09 , 3 , 0.95 ),
104 (64, "Gadolinium", "Gd", ( 0.27, 1.0, 0.78), 1.61, 1.61, 2.54 , 3 , 0.93 ),
105 (65, "Terbium", "Tb", ( 0.18, 1.0, 0.78), 1.59, 1.59, 2.51 , 3 , 0.92 , 4 , 0.84 ),
106 (66, "Dysprosium", "Dy", ( 0.12, 1.0, 0.78), 1.59, 1.59, 2.49 , 3 , 0.90 ),
107 (67, "Holmium", "Ho", ( 0.0, 1.0, 0.61), 1.58, 1.58, 2.47 , 3 , 0.89 ),
108 (68, "Erbium", "Er", ( 0.0, 0.90, 0.45), 1.57, 1.57, 2.45 , 3 , 0.88 ),
109 (69, "Thulium", "Tm", ( 0.0, 0.83, 0.32), 1.56, 1.56, 2.42 , 3 , 0.87 ),
110 (70, "Ytterbium", "Yb", ( 0.0, 0.74, 0.21), 1.74, 1.74, 2.40 , 2 , 0.93 , 3 , 0.85 ),
111 (71, "Lutetium", "Lu", ( 0.0, 0.67, 0.14), 1.56, 1.56, 2.25 , 3 , 0.85 ),
112 (72, "Hafnium", "Hf", ( 0.30, 0.76, 1.0), 1.44, 1.44, 2.16 , 4 , 0.78 ),
113 (73, "Tantalum", "Ta", ( 0.30, 0.65, 1.0), 1.34, 1.34, 2.09 , 5 , 0.68 ),
114 (74, "Tungsten", "W", ( 0.12, 0.58, 0.83), 1.30, 1.30, 2.02 , 4 , 0.70 , 6 , 0.62 ),
115 (75, "Rhenium", "Re", ( 0.14, 0.49, 0.67), 1.28, 1.28, 1.97 , 4 , 0.72 , 7 , 0.56 ),
116 (76, "Osmium", "Os", ( 0.14, 0.4, 0.58), 1.26, 1.26, 1.92 , 4 , 0.88 , 6 , 0.69 ),
117 (77, "Iridium", "Ir", ( 0.09, 0.32, 0.52), 1.27, 1.27, 1.87 , 4 , 0.68 ),
118 (78, "Platinium", "Pt", ( 0.81, 0.81, 0.87), 1.30, 1.30, 1.83 , 2 , 0.80 , 4 , 0.65 ),
119 (79, "Gold", "Au", ( 1.0, 0.81, 0.13), 1.34, 1.34, 1.79 , 1 , 1.37 , 3 , 0.85 ),
120 (80, "Mercury", "Hg", ( 0.72, 0.72, 0.81), 1.49, 1.49, 1.76 , 1 , 1.27 , 2 , 1.10 ),
121 (81, "Thallium", "Tl", ( 0.65, 0.32, 0.30), 1.48, 1.48, 2.08 , 1 , 1.47 , 3 , 0.95 ),
122 (82, "Lead", "Pb", ( 0.34, 0.34, 0.38), 1.47, 1.47, 1.81 , 2 , 1.20 , 4 , 0.84 ),
123 (83, "Bismuth", "Bi", ( 0.61, 0.30, 0.70), 1.46, 1.46, 1.63 , 1 , 0.98 , 3 , 0.96 , 5 , 0.74 ),
124 (84, "Polonium", "Po", ( 0.67, 0.36, 0.0), 1.46, 1.46, 1.53 , 6 , 0.67 ),
125 (85, "Astatine", "At", ( 0.45, 0.30, 0.27), 1.45, 1.45, 1.43 , -3 , 2.22 , 3 , 0.85 , 5 , 0.46 ),
126 (86, "Radon", "Rn", ( 0.25, 0.50, 0.58), 1.00, 1.00, 1.34 ),
127 (87, "Francium", "Fr", ( 0.25, 0.0, 0.4), 1.00, 1.00, 1.00 , 1 , 1.80 ),
128 (88, "Radium", "Ra", ( 0.0, 0.49, 0.0), 1.00, 1.00, 1.00 , 2 , 1.43 ),
129 (89, "Actinium", "Ac", ( 0.43, 0.67, 0.98), 1.00, 1.00, 1.00 , 3 , 1.18 ),
130 (90, "Thorium", "Th", ( 0.0, 0.72, 1.0), 1.65, 1.65, 1.00 , 4 , 1.02 ),
131 (91, "Protactinium", "Pa", ( 0.0, 0.63, 1.0), 1.00, 1.00, 1.00 , 3 , 1.13 , 4 , 0.98 , 5 , 0.89 ),
132 (92, "Uranium", "U", ( 0.0, 0.56, 1.0), 1.42, 1.42, 1.00 , 4 , 0.97 , 6 , 0.80 ),
133 (93, "Neptunium", "Np", ( 0.0, 0.50, 1.0), 1.00, 1.00, 1.00 , 3 , 1.10 , 4 , 0.95 , 7 , 0.71 ),
134 (94, "Plutonium", "Pu", ( 0.0, 0.41, 1.0), 1.00, 1.00, 1.00 , 3 , 1.08 , 4 , 0.93 ),
135 (95, "Americium", "Am", ( 0.32, 0.36, 0.94), 1.00, 1.00, 1.00 , 3 , 1.07 , 4 , 0.92 ),
136 (96, "Curium", "Cm", ( 0.47, 0.36, 0.89), 1.00, 1.00, 1.00 ),
137 (97, "Berkelium", "Bk", ( 0.54, 0.30, 0.89), 1.00, 1.00, 1.00 ),
138 (98, "Californium", "Cf", ( 0.63, 0.21, 0.83), 1.00, 1.00, 1.00 ),
139 (99, "Einsteinium", "Es", ( 0.70, 0.12, 0.83), 1.00, 1.00, 1.00 ),
140 (100, "Fermium", "Fm", ( 0.70, 0.12, 0.72), 1.00, 1.00, 1.00 ),
141 (101, "Mendelevium", "Md", ( 0.70, 0.05, 0.65), 1.00, 1.00, 1.00 ),
142 (102, "Nobelium", "No", ( 0.74, 0.05, 0.52), 1.00, 1.00, 1.00 ),
143 (103, "Lawrencium", "Lr", ( 0.78, 0.0, 0.4), 1.00, 1.00, 1.00 ),
144 (104, "Vacancy", "Vac", ( 0.5, 0.5, 0.5), 1.00, 1.00, 1.00),
145 (105, "Default", "Default", ( 1.0, 1.0, 1.0), 1.00, 1.00, 1.00),
146 (106, "Stick", "Stick", ( 0.5, 0.5, 0.5), 1.00, 1.00, 1.00),
149 # This list here contains all data of the elements and will be used during
150 # runtime. It is a list of classes.
151 # During executing Atomic Blender, the list will be initialized with the fixed
152 # data from above via the class structure below (ElementProp). We
153 # have then one fixed list (above), which will never be changed, and a list of
154 # classes with same data. The latter can be modified via loading a separate
158 # This is the class, which stores the properties for one element.
159 class ElementProp(object):
160 __slots__
= ('number', 'name', 'short_name', 'color', 'radii', 'radii_ionic')
161 def __init__(self
, number
, name
, short_name
, color
, radii
, radii_ionic
):
164 self
.short_name
= short_name
167 self
.radii_ionic
= radii_ionic
169 # This is the class, which stores the properties of one atom.
170 class AtomProp(object):
171 __slots__
= ('element', 'name', 'location', 'radius', 'color', 'material')
172 def __init__(self
, element
, name
, location
, radius
, color
, material
):
173 self
.element
= element
175 self
.location
= location
178 self
.material
= material
180 # This is the class, which stores the two atoms of one stick.
181 class StickProp(object):
182 __slots__
= ('atom1', 'atom2', 'number', 'dist')
183 def __init__(self
, atom1
, atom2
, number
, dist
):
189 # -----------------------------------------------------------------------------
190 # Some basic routines
193 # The function, which reads all necessary properties of the elements.
198 for item
in ELEMENTS_DEFAULT
:
200 # All three radii into a list
201 radii
= [item
[4],item
[5],item
[6]]
202 # The handling of the ionic radii will be done later. So far, it is an
206 li
= ElementProp(item
[0],item
[1],item
[2],item
[3],
211 # The function, which reads the x,y,z positions of all atoms in a PDB
214 # filepath_pdb: path to pdb file
215 # radiustype : '0' default
218 def read_pdb_file(filepath_pdb
, radiustype
):
220 # The list of all atoms as read from the PDB file.
223 # Open the pdb file ...
224 filepath_pdb_p
= open(filepath_pdb
, "r")
226 #Go to the line, in which "ATOM" or "HETATM" appears.
227 for line
in filepath_pdb_p
:
228 split_list
= line
.split(' ')
229 if "ATOM" in split_list
[0]:
231 if "HETATM" in split_list
[0]:
235 # This is in fact an endless 'while loop', ...
238 # ... the loop is broken here (EOF) ...
242 # If there is a "TER" we need to put empty entries into the lists
243 # in order to not destroy the order of atom numbers and same numbers
244 # used for sticks. "TER? What is that?" TER indicates the end of a
245 # list of ATOM/HETATM records for a chain.
251 location
= Vector((0,0,0))
252 # Append the TER into the list. Material remains empty so far.
253 all_atoms
.append(AtomProp(short_name
,
259 # If 'ATOM or 'HETATM' appears in the line then do ...
260 elif "ATOM" in line
or "HETATM" in line
:
262 # What follows is due to deviations which appear from PDB to
263 # PDB file. It is very special!
265 # PLEASE, DO NOT CHANGE! ............................... from here
266 if line
[12:13] == " " or line
[12:13].isdigit() == True:
267 short_name
= line
[13:14]
268 if line
[14:15].islower() == True:
269 short_name
= short_name
+ line
[14:15]
270 elif line
[12:13].isupper() == True:
271 short_name
= line
[12:13]
272 if line
[13:14].isalpha() == True:
273 short_name
= short_name
+ line
[13:14]
275 print("Atomic Blender: Strange error in PDB file.\n"
276 "Look for element names at positions 13-16 and 78-79.\n")
281 if line
[76:77] == " ":
282 short_name2
= line
[76:77]
284 short_name2
= line
[76:78]
286 if short_name2
.isalpha() == True:
288 for element
in ELEMENTS
:
289 if str.upper(short_name2
) == str.upper(element
.short_name
):
293 short_name
= short_name2
294 # ....................................................... to here.
296 # Go through all elements and find the element of the current atom.
298 for element
in ELEMENTS
:
299 if str.upper(short_name
) == str.upper(element
.short_name
):
300 # Give the atom its proper names, color and radius:
301 short_name
= str.upper(element
.short_name
)
303 # int(radiustype) => type of radius:
304 # pre-defined (0), atomic (1) or van der Waals (2)
305 radius
= float(element
.radii
[int(radiustype
)])
306 color
= element
.color
310 # Is it a vacancy or an 'unknown atom' ?
311 if FLAG_FOUND
== False:
312 # Give this atom also a name. If it is an 'X' then it is a
313 # vacancy. Otherwise ...
314 if "X" in short_name
:
317 radius
= float(ELEMENTS
[-3].radii
[int(radiustype
)])
318 color
= ELEMENTS
[-3].color
319 # ... take what is written in the PDB file. These are somewhat
320 # unknown atoms. This should never happen, the element list is
321 # almost complete. However, we do this due to security reasons.
323 short_name
= str.upper(short_name
)
324 name
= str.upper(short_name
)
325 radius
= float(ELEMENTS
[-2].radii
[int(radiustype
)])
326 color
= ELEMENTS
[-2].color
328 # x,y and z are at fixed positions in the PDB file.
329 x
= float(line
[30:38].rsplit()[0])
330 y
= float(line
[38:46].rsplit()[0])
331 z
= float(line
[46:55].rsplit()[0])
333 location
= Vector((x
,y
,z
))
337 # Append the atom to the list. Material remains empty so far.
338 all_atoms
.append(AtomProp(short_name
,
344 line
= filepath_pdb_p
.readline()
347 filepath_pdb_p
.close()
348 # From above it can be clearly seen that j is now the number of all atoms.
349 Number_of_total_atoms
= j
351 return (Number_of_total_atoms
, all_atoms
)
354 # The function, which reads the sticks in a PDB file.
355 def read_pdb_file_sticks(filepath_pdb
, use_sticks_bonds
, all_atoms
):
357 # The list of all sticks.
361 filepath_pdb_p
= open(filepath_pdb
, "r")
363 line
= filepath_pdb_p
.readline()
364 split_list
= line
.split(' ')
366 # Go to the first entry
367 if "CONECT" not in split_list
[0]:
368 for line
in filepath_pdb_p
:
369 split_list
= line
.split(' ')
370 if "CONECT" in split_list
[0]:
376 # This is in fact an endless while loop, ...
379 # ... which is broken here (EOF) ...
382 # ... or here, when no 'CONECT' appears anymore.
383 if "CONECT" not in line
:
386 # The strings of the atom numbers do have a clear position in the file
387 # (From 7 to 12, from 13 to 18 and so on.) and one needs to consider
388 # this. One could also use the split function but then one gets into
389 # trouble if there are lots of atoms: For instance, it may happen that
391 # CONECT 11111 22244444
393 # In Fact it means that atom No. 11111 has a connection with atom
394 # No. 222 but also with atom No. 44444. The split function would give
395 # me only two numbers (11111 and 22244444), which is wrong.
397 # Cut spaces from the right and 'CONECT' at the beginning
402 loops
= int(length
/5)
406 for i
in range(loops
):
407 number
= line
[5*i
:5*(i
+1)].rsplit()
409 if number
[0].isdigit() == True:
410 atom_number
= int(number
[0])
411 atom_list
.append(atom_number
)
413 # The first atom is connected with all the others in the list.
416 # For all the other atoms in the list do:
417 for atom2
in atom_list
[1:]:
419 if use_sticks_bonds
== True:
420 number
= atom_list
[1:].count(atom2
)
422 if number
== 2 or number
== 3:
423 basis_list
= list(set(atom_list
[1:]))
425 if len(basis_list
) > 1:
426 basis1
= (all_atoms
[atom1
-1].location
427 - all_atoms
[basis_list
[0]-1].location
)
428 basis2
= (all_atoms
[atom1
-1].location
429 - all_atoms
[basis_list
[1]-1].location
)
430 plane_n
= basis1
.cross(basis2
)
432 dist_n
= (all_atoms
[atom1
-1].location
433 - all_atoms
[atom2
-1].location
)
434 dist_n
= dist_n
.cross(plane_n
)
435 dist_n
= dist_n
/ dist_n
.length
437 dist_n
= (all_atoms
[atom1
-1].location
438 - all_atoms
[atom2
-1].location
)
439 dist_n
= Vector((dist_n
[1],-dist_n
[0],0))
440 dist_n
= dist_n
/ dist_n
.length
450 # Note that in a PDB file, sticks of one atom pair can appear a
451 # couple of times. (Only god knows why ...)
452 # So, does a stick between the considered atoms already exist?
454 for k
in range(Number_of_sticks
):
455 if ((all_sticks
[k
].atom1
== atom1
and all_sticks
[k
].atom2
== atom2
) or
456 (all_sticks
[k
].atom2
== atom1
and all_sticks
[k
].atom1
== atom2
)):
458 # If yes, then FLAG on 'True'.
462 # If the stick is not yet registered (FLAG_BAR == False), then
464 if FLAG_BAR
== False:
465 all_sticks
.append(StickProp(atom1
,atom2
,number
,dist_n
))
466 Number_of_sticks
+= 1
469 line
= filepath_pdb_p
.readline()
472 filepath_pdb_p
.close()
477 # Function, which produces a cylinder. All is somewhat easy to undertsand.
478 def build_stick(radius
, length
, sectors
):
480 dphi
= 2.0 * pi
/(float(sectors
)-1)
483 vertices_top
= [Vector((0,0,length
/ 2.0))]
484 vertices_bottom
= [Vector((0,0,-length
/ 2.0))]
486 for i
in range(sectors
-1):
487 x
= radius
* cos( dphi
* i
)
488 y
= radius
* sin( dphi
* i
)
490 vertex
= Vector((x
,y
,z
))
491 vertices_top
.append(vertex
)
493 vertex
= Vector((x
,y
,z
))
494 vertices_bottom
.append(vertex
)
495 vertices
= vertices_top
+ vertices_bottom
497 # Side facets (Cylinder)
499 for i
in range(sectors
-1):
501 faces1
.append( [i
+1, 1, 1+sectors
, i
+1+sectors
] )
503 faces1
.append( [i
+1, i
+2, i
+2+sectors
, i
+1+sectors
] )
507 for i
in range(sectors
-1):
509 face_top
= [0,sectors
-1,1]
510 face_bottom
= [sectors
,2*sectors
-1,sectors
+1]
513 face_bottom
= [sectors
]
515 face_top
.append(i
+j
+1)
516 face_bottom
.append(i
+j
+1+sectors
)
517 faces2
.append(face_top
)
518 faces2
.append(face_bottom
)
520 # Build the mesh, Cylinder
521 cylinder
= bpy
.data
.meshes
.new("Sticks_Cylinder")
522 cylinder
.from_pydata(vertices
, [], faces1
)
524 new_cylinder
= bpy
.data
.objects
.new("Sticks_Cylinder", cylinder
)
525 bpy
.context
.scene
.objects
.link(new_cylinder
)
527 # Build the mesh, Cups
528 cups
= bpy
.data
.meshes
.new("Sticks_Cups")
529 cups
.from_pydata(vertices
, [], faces2
)
531 new_cups
= bpy
.data
.objects
.new("Sticks_Cups", cups
)
532 bpy
.context
.scene
.objects
.link(new_cups
)
534 return (new_cylinder
, new_cups
)
537 # Function, which puts a camera and light source into the 3D scene
538 def camera_light_source(use_camera
,
545 # If chosen a camera is put into the scene.
546 if use_camera
== True:
548 # Assume that the object is put into the global origin. Then, the
549 # camera is moved in x and z direction, not in y. The object has its
550 # size at distance sqrt(object_size) from the origin. So, move the
551 # camera by this distance times a factor of camera_factor in x and z.
552 # Then add x, y and z of the origin of the object.
553 object_camera_vec
= Vector((sqrt(object_size
) * camera_factor
,
555 sqrt(object_size
) * camera_factor
))
556 camera_xyz_vec
= object_center_vec
+ object_camera_vec
559 current_layers
=bpy
.context
.scene
.layers
560 camera_data
= bpy
.data
.cameras
.new("A_camera")
561 camera_data
.lens
= 45
562 camera_data
.clip_end
= 500.0
563 camera
= bpy
.data
.objects
.new("A_camera", camera_data
)
564 camera
.location
= camera_xyz_vec
565 camera
.layers
= current_layers
566 bpy
.context
.scene
.objects
.link(camera
)
568 # Here the camera is rotated such it looks towards the center of
569 # the object. The [0.0, 0.0, 1.0] vector along the z axis
570 z_axis_vec
= Vector((0.0, 0.0, 1.0))
571 # The angle between the last two vectors
572 angle
= object_camera_vec
.angle(z_axis_vec
, 0)
573 # The cross-product of z_axis_vec and object_camera_vec
574 axis_vec
= z_axis_vec
.cross(object_camera_vec
)
575 # Rotate 'axis_vec' by 'angle' and convert this to euler parameters.
576 # 4 is the size of the matrix.
577 camera
.rotation_euler
= Matrix
.Rotation(angle
, 4, axis_vec
).to_euler()
579 # Rotate the camera around its axis by 90° such that we have a nice
580 # camera position and view onto the object.
581 bpy
.ops
.object.select_all(action
='DESELECT')
583 bpy
.ops
.transform
.rotate(value
=(90.0*2*pi
/360.0),
584 axis
=object_camera_vec
,
585 constraint_axis
=(False, False, False),
586 constraint_orientation
='GLOBAL',
587 mirror
=False, proportional
='DISABLED',
588 proportional_edit_falloff
='SMOOTH',
589 proportional_size
=1, snap
=False,
590 snap_target
='CLOSEST', snap_point
=(0, 0, 0),
591 snap_align
=False, snap_normal
=(0, 0, 0),
592 release_confirm
=False)
594 # Here a lamp is put into the scene, if chosen.
595 if use_light
== True:
597 # This is the distance from the object measured in terms of %
598 # of the camera distance. It is set onto 50% (1/2) distance.
599 light_dl
= sqrt(object_size
) * 15 * 0.5
600 # This is a factor to which extend the lamp shall go to the right
601 # (from the camera point of view).
602 light_dy_right
= light_dl
* (3.0/4.0)
604 # Create x, y and z for the lamp.
605 object_light_vec
= Vector((light_dl
,light_dy_right
,light_dl
))
606 light_xyz_vec
= object_center_vec
+ object_light_vec
609 current_layers
=bpy
.context
.scene
.layers
610 light_data
= bpy
.data
.lights
.new(name
="A_light", type="POINT")
611 light_data
.distance
= 500.0
612 light_data
.energy
= 3.0
613 light_data
.shadow_method
= 'RAY_SHADOW'
614 lamp
= bpy
.data
.objects
.new("A_light", light_data
)
615 lamp
.location
= light_xyz_vec
616 lamp
.layers
= current_layers
617 bpy
.context
.scene
.objects
.link(lamp
)
619 # Some settings for the World: a bit ambient occlusion
620 bpy
.context
.scene
.world
.light_settings
.use_ambient_occlusion
= True
621 bpy
.context
.scene
.world
.light_settings
.ao_factor
= 0.2
624 # Function, which draws the atoms of one type (balls). This is one
625 # dupliverts structure then.
626 # Return: the dupliverts structure
627 def draw_atoms_one_type(draw_all_atoms_type
,
634 # Create first the vertices composed of the coordinates of all
637 for atom
in draw_all_atoms_type
:
638 # In fact, the object is created in the World's origin.
639 # This is why 'object_center_vec' is subtracted. At the end
640 # the whole object is translated back to 'object_center_vec'.
641 atom_vertices
.append(atom
[2] - object_center_vec
)
644 atom_mesh
= bpy
.data
.meshes
.new("Mesh_"+atom
[0])
645 atom_mesh
.from_pydata(atom_vertices
, [], [])
647 new_atom_mesh
= bpy
.data
.objects
.new(atom
[0], atom_mesh
)
648 bpy
.context
.scene
.objects
.link(new_atom_mesh
)
650 # Now, build a representative sphere (atom).
651 current_layers
= bpy
.context
.scene
.layers
653 if atom
[0] == "Vacancy":
654 bpy
.ops
.mesh
.primitive_cube_add(
655 view_align
=False, enter_editmode
=False,
656 location
=(0.0, 0.0, 0.0),
657 rotation
=(0.0, 0.0, 0.0),
658 layers
=current_layers
)
662 bpy
.ops
.surface
.primitive_nurbs_surface_sphere_add(
663 view_align
=False, enter_editmode
=False,
664 location
=(0,0,0), rotation
=(0.0, 0.0, 0.0),
665 layers
=current_layers
)
667 elif Ball_type
== "1":
668 bpy
.ops
.mesh
.primitive_uv_sphere_add(
669 segments
=Ball_azimuth
, ring_count
=Ball_zenith
,
670 size
=1, view_align
=False, enter_editmode
=False,
671 location
=(0,0,0), rotation
=(0, 0, 0),
672 layers
=current_layers
)
674 elif Ball_type
== "2":
675 bpy
.ops
.object.metaball_add(type='BALL', view_align
=False,
676 enter_editmode
=False, location
=(0, 0, 0),
677 rotation
=(0, 0, 0), layers
=current_layers
)
679 ball
= bpy
.context
.scene
.objects
.active
680 ball
.scale
= (atom
[3]*Ball_radius_factor
,) * 3
682 if atom
[0] == "Vacancy":
683 ball
.name
= "Cube_"+atom
[0]
685 ball
.name
= "Ball_"+atom
[0]
686 ball
.active_material
= atom
[1]
687 ball
.parent
= new_atom_mesh
688 new_atom_mesh
.dupli_type
= 'VERTS'
689 # The object is back translated to 'object_center_vec'.
690 new_atom_mesh
.location
= object_center_vec
695 # Function, which draws the sticks with help of the dupliverts technique.
696 # Return: list of dupliverts structures.
697 def draw_sticks_dupliverts(all_atoms
,
710 if use_sticks_color
== False:
711 bpy
.ops
.object.material_slot_add()
712 stick_material
= bpy
.data
.materials
.new(ELEMENTS
[-1].name
)
713 stick_material
.diffuse_color
= ELEMENTS
[-1].color
715 # Sort the sticks and put them into a new list such that ...
716 sticks_all_lists
= []
717 if use_sticks_color
== True:
718 for atom_type
in atom_all_types_list
:
719 if atom_type
[0] == "TER":
722 for stick
in all_sticks
:
724 for repeat
in range(stick
.number
):
726 atom1
= copy(all_atoms
[stick
.atom1
-1].location
)-center
727 atom2
= copy(all_atoms
[stick
.atom2
-1].location
)-center
729 dist
= Stick_diameter
* Stick_dist
731 if stick
.number
== 2:
733 atom1
+= (stick
.dist
* dist
)
734 atom2
+= (stick
.dist
* dist
)
736 atom1
-= (stick
.dist
* dist
)
737 atom2
-= (stick
.dist
* dist
)
739 if stick
.number
== 3:
741 atom1
+= (stick
.dist
* dist
)
742 atom2
+= (stick
.dist
* dist
)
744 atom1
-= (stick
.dist
* dist
)
745 atom2
-= (stick
.dist
* dist
)
749 if atom_type
[0] == all_atoms
[stick
.atom1
-1].name
:
751 name
= "_" + all_atoms
[stick
.atom1
-1].name
752 material
= all_atoms
[stick
.atom1
-1].material
753 sticks_list
.append([name
, location
, dv
, material
])
754 if atom_type
[0] == all_atoms
[stick
.atom2
-1].name
:
755 location
= atom1
- n
* dl
* int(ceil(dv
.length
/ (2.0 * dl
)))
756 name
= "_" + all_atoms
[stick
.atom2
-1].name
757 material
= all_atoms
[stick
.atom2
-1].material
758 sticks_list
.append([name
, location
, dv
, material
])
760 if sticks_list
!= []:
761 sticks_all_lists
.append(sticks_list
)
764 for stick
in all_sticks
:
769 for repeat
in range(stick
.number
):
771 atom1
= copy(all_atoms
[stick
.atom1
-1].location
)-center
772 atom2
= copy(all_atoms
[stick
.atom2
-1].location
)-center
774 dist
= Stick_diameter
* Stick_dist
776 if stick
.number
== 2:
778 atom1
+= (stick
.dist
* dist
)
779 atom2
+= (stick
.dist
* dist
)
781 atom1
-= (stick
.dist
* dist
)
782 atom2
-= (stick
.dist
* dist
)
783 if stick
.number
== 3:
785 atom1
+= (stick
.dist
* dist
)
786 atom2
+= (stick
.dist
* dist
)
788 atom1
-= (stick
.dist
* dist
)
789 atom2
-= (stick
.dist
* dist
)
794 material
= stick_material
795 sticks_list
.append(["", location
, dv
, material
])
797 sticks_all_lists
.append(sticks_list
)
799 atom_object_list
= []
800 # ... the sticks in the list can be drawn:
801 for stick_list
in sticks_all_lists
:
806 # What follows is school mathematics! :-)
807 for stick
in stick_list
:
816 if use_sticks_color
== True:
817 loops
= int(ceil(dv
.length
/ (2.0 * dl
)))
819 loops
= int(ceil(dv
.length
/ dl
))
821 for j
in range(loops
):
823 g
= v1
- n
* dl
/ 2.0 - n
* dl
* j
824 p1
= g
+ n_b
* Stick_diameter
825 p2
= g
- n_b
* Stick_diameter
826 p3
= g
- n_b
.cross(n
) * Stick_diameter
827 p4
= g
+ n_b
.cross(n
) * Stick_diameter
833 faces
.append((i
*4+0,i
*4+2,i
*4+1,i
*4+3))
837 mesh
= bpy
.data
.meshes
.new("Sticks"+stick
[0])
838 mesh
.from_pydata(vertices
, [], faces
)
840 new_mesh
= bpy
.data
.objects
.new("Sticks"+stick
[0], mesh
)
841 bpy
.context
.scene
.objects
.link(new_mesh
)
844 # Get the cylinder from the 'build_stick' function.
845 object_stick
= build_stick(Stick_diameter
, dl
, Stick_sectors
)
846 stick_cylinder
= object_stick
[0]
847 stick_cylinder
.active_material
= stick
[3]
848 stick_cups
= object_stick
[1]
849 stick_cups
.active_material
= stick
[3]
851 # Smooth the cylinders.
852 if use_sticks_smooth
== True:
853 bpy
.ops
.object.select_all(action
='DESELECT')
854 stick_cylinder
.select
= True
855 stick_cups
.select
= True
856 bpy
.ops
.object.shade_smooth()
858 # Parenting the mesh to the cylinder.
859 stick_cylinder
.parent
= new_mesh
860 stick_cups
.parent
= new_mesh
861 new_mesh
.dupli_type
= 'FACES'
862 new_mesh
.location
= center
863 atom_object_list
.append(new_mesh
)
865 # Return the list of dupliverts structures.
866 return atom_object_list
869 # Function, which draws the sticks with help of the skin and subdivision
871 def draw_sticks_skin(all_atoms
,
876 sticks_subdiv_render
):
878 # These counters are for the edges, in the shape [i,i+1].
881 # This is the list of vertices, containing the atom position
884 # This is the 'same' list, which contains not vector position of
885 # the atoms but their numbers. It is used to handle the edges.
886 stick_vertices_nr
= []
887 # This is the list of edges.
890 # Go through the list of all sticks. For each stick do:
891 for stick
in all_sticks
:
893 # Each stick has two atoms = two vertices.
896 [ 0,1 , 3,4 , 0,8 , 7,3]
897 [[0,1], [2,3], [4,5], [6,7]]
899 [ 0,1 , 3,4 , x,8 , 7,x] x:deleted
900 [[0,1], [2,3], [0,5], [6,2]]
903 # Check, if the vertex (atom) is already in the vertex list.
907 for stick2
in stick_vertices_nr
:
908 if stick2
== stick
.atom1
-1:
914 for stick2
in stick_vertices_nr
:
915 if stick2
== stick
.atom2
-1:
920 # If the vertex (atom) is not yet in the vertex list:
921 # append the number of atom and the vertex to the two lists.
922 # For the first atom:
924 atom1
= copy(all_atoms
[stick
.atom1
-1].location
)
925 stick_vertices
.append(atom1
)
926 stick_vertices_nr
.append(stick
.atom1
-1)
927 # For the second atom:
929 atom2
= copy(all_atoms
[stick
.atom2
-1].location
)
930 stick_vertices
.append(atom2
)
931 stick_vertices_nr
.append(stick
.atom2
-1)
935 # If both vertices (atoms) were not in the lists, then
936 # the edge is simply [i,i+1]. These are two new vertices
937 # (atoms), so increase i by 2.
938 if FLAG_s1
== False and FLAG_s2
== False:
939 stick_edges
.append([i
,i
+1])
941 # Both vertices (atoms) were already in the list, so then
942 # use the vertices (atoms), which already exist. They are
943 # at positions s1 and s2.
944 if FLAG_s1
== True and FLAG_s2
== True:
945 stick_edges
.append([s1
,s2
])
946 # The following two if cases describe the situation that
947 # only one vertex (atom) was in the list. Since only ONE
948 # new vertex was added, increase i by one.
949 if FLAG_s1
== True and FLAG_s2
== False:
950 stick_edges
.append([s1
,i
])
952 if FLAG_s1
== False and FLAG_s2
== True:
953 stick_edges
.append([i
,s2
])
956 # Build the mesh of the sticks
957 stick_mesh
= bpy
.data
.meshes
.new("Mesh_sticks")
958 stick_mesh
.from_pydata(stick_vertices
, stick_edges
, [])
960 new_stick_mesh
= bpy
.data
.objects
.new("Sticks", stick_mesh
)
961 bpy
.context
.scene
.objects
.link(new_stick_mesh
)
963 # Apply the skin modifier.
964 new_stick_mesh
.modifiers
.new(name
="Sticks_skin", type='SKIN')
965 # Smooth the skin surface if this option has been chosen.
966 new_stick_mesh
.modifiers
[0].use_smooth_shade
= use_sticks_smooth
967 # Apply the Subdivision modifier.
968 new_stick_mesh
.modifiers
.new(name
="Sticks_subsurf", type='SUBSURF')
969 # Options: choose the levels
970 new_stick_mesh
.modifiers
[1].levels
= sticks_subdiv_view
971 new_stick_mesh
.modifiers
[1].render_levels
= sticks_subdiv_render
973 bpy
.ops
.object.material_slot_add()
974 stick_material
= bpy
.data
.materials
.new(ELEMENTS
[-1].name
)
975 stick_material
.diffuse_color
= ELEMENTS
[-1].color
976 new_stick_mesh
.active_material
= stick_material
978 # This is for putting the radiu of the sticks onto
979 # the desired value 'Stick_diameter'
980 bpy
.context
.scene
.objects
.active
= new_stick_mesh
982 bpy
.ops
.object.mode_set(mode
='EDIT', toggle
=False)
983 bm
= bmesh
.from_edit_mesh(new_stick_mesh
.data
)
984 bpy
.ops
.mesh
.select_all(action
='DESELECT')
986 # Select all vertices
990 # This is somewhat a factor for the radius.
992 # Apply operator 'skin_resize'.
993 bpy
.ops
.transform
.skin_resize(value
=(Stick_diameter
*r_f
,
996 constraint_axis
=(False, False, False),
997 constraint_orientation
='GLOBAL',
999 proportional
='DISABLED',
1000 proportional_edit_falloff
='SMOOTH',
1001 proportional_size
=1,
1003 snap_target
='CLOSEST',
1004 snap_point
=(0, 0, 0),
1006 snap_normal
=(0, 0, 0),
1007 texture_space
=False,
1008 release_confirm
=False)
1009 # Back to the OBJECT mode.
1010 bpy
.ops
.object.mode_set(mode
='OBJECT', toggle
=False)
1012 return new_stick_mesh
1015 # Draw the sticks the normal way: connect the atoms by simple cylinders.
1016 # Two options: 1. single cylinders parented to an empty
1017 # 2. one single mesh object
1018 def draw_sticks_normal(all_atoms
,
1024 use_sticks_one_object
,
1025 use_sticks_one_object_nr
):
1027 bpy
.ops
.object.material_slot_add()
1028 stick_material
= bpy
.data
.materials
.new(ELEMENTS
[-1].name
)
1029 stick_material
.diffuse_color
= ELEMENTS
[-1].color
1031 up_axis
= Vector([0.0, 0.0, 1.0])
1032 current_layers
= bpy
.context
.scene
.layers
1034 # For all sticks, do ...
1038 for stick
in all_sticks
:
1040 # The vectors of the two atoms
1041 atom1
= all_atoms
[stick
.atom1
-1].location
-center
1042 atom2
= all_atoms
[stick
.atom2
-1].location
-center
1044 location
= (atom1
+ atom2
) * 0.5
1045 # The difference of both vectors
1047 # Angle with respect to the z-axis
1048 angle
= v
.angle(up_axis
, 0)
1049 # Cross-product between v and the z-axis vector. It is the
1050 # vector of rotation.
1051 axis
= up_axis
.cross(v
)
1052 # Calculate Euler angles
1053 euler
= Matrix
.Rotation(angle
, 4, axis
).to_euler()
1055 bpy
.ops
.mesh
.primitive_cylinder_add(vertices
=Stick_sectors
,
1056 radius
=Stick_diameter
,
1058 end_fill_type
='NGON',
1060 enter_editmode
=False,
1063 layers
=current_layers
)
1064 # Put the stick into the scene ...
1065 stick
= bpy
.context
.scene
.objects
.active
1066 # ... and rotate the stick.
1067 stick
.rotation_euler
= euler
1069 stick
.name
= "Stick_Cylinder"
1072 # Smooth the cylinder.
1073 if use_sticks_smooth
== True:
1074 bpy
.ops
.object.select_all(action
='DESELECT')
1076 bpy
.ops
.object.shade_smooth()
1078 list_group_sub
.append(stick
)
1080 if use_sticks_one_object
== True:
1081 if counter
== use_sticks_one_object_nr
:
1082 bpy
.ops
.object.select_all(action
='DESELECT')
1083 for stick
in list_group_sub
:
1085 bpy
.ops
.object.join()
1086 list_group
.append(bpy
.context
.scene
.objects
.active
)
1087 bpy
.ops
.object.select_all(action
='DESELECT')
1092 stick
.active_material
= stick_material
1094 if use_sticks_one_object
== True:
1095 bpy
.ops
.object.select_all(action
='DESELECT')
1096 for stick
in list_group_sub
:
1098 bpy
.ops
.object.join()
1099 list_group
.append(bpy
.context
.scene
.objects
.active
)
1100 bpy
.ops
.object.select_all(action
='DESELECT')
1102 for group
in list_group
:
1104 bpy
.ops
.object.join()
1105 bpy
.ops
.object.origin_set(type='ORIGIN_GEOMETRY',
1107 sticks
= bpy
.context
.scene
.objects
.active
1108 sticks
.active_material
= stick_material
1110 bpy
.ops
.object.empty_add(type='ARROWS',
1114 layers
=current_layers
)
1115 sticks
= bpy
.context
.scene
.objects
.active
1116 for stick
in list_group_sub
:
1117 stick
.parent
= sticks
1119 sticks
.name
= "Sticks"
1120 sticks
.location
+= center
1125 # -----------------------------------------------------------------------------
1128 def import_pdb(Ball_type
,
1133 Ball_distance_factor
,
1137 sticks_subdiv_render
,
1141 use_sticks_one_object
,
1142 use_sticks_one_object_nr
,
1143 Stick_unit
, Stick_dist
,
1153 atom_material_list
= []
1155 # A list of ALL objects which are loaded (needed for selecting the loaded
1157 atom_object_list
= []
1159 # ------------------------------------------------------------------------
1160 # INITIALIZE THE ELEMENT LIST
1164 # ------------------------------------------------------------------------
1165 # READING DATA OF ATOMS
1167 (Number_of_total_atoms
, all_atoms
) = read_pdb_file(filepath_pdb
, radiustype
)
1169 # ------------------------------------------------------------------------
1170 # MATERIAL PROPERTIES FOR ATOMS
1172 # The list that contains info about all types of atoms is created
1173 # here. It is used for building the material properties for
1174 # instance (see below).
1175 atom_all_types_list
= []
1177 for atom
in all_atoms
:
1179 for atom_type
in atom_all_types_list
:
1180 # If the atom name is already in the list, FLAG on 'True'.
1181 if atom_type
[0] == atom
.name
:
1184 # No name in the current list has been found? => New entry.
1185 if FLAG_FOUND
== False:
1186 # Stored are: Atom label (e.g. 'Na'), the corresponding atom
1187 # name (e.g. 'Sodium') and its color.
1188 atom_all_types_list
.append([atom
.name
, atom
.element
, atom
.color
])
1190 # The list of materials is built.
1191 # Note that all atoms of one type (e.g. all hydrogens) get only ONE
1192 # material! This is good because then, by activating one atom in the
1193 # Blender scene and changing the color of this atom, one changes the color
1194 # of ALL atoms of the same type at the same time.
1196 # Create first a new list of materials for each type of atom
1198 for atom_type
in atom_all_types_list
:
1199 material
= bpy
.data
.materials
.new(atom_type
[1])
1200 material
.name
= atom_type
[0]
1201 material
.diffuse_color
= atom_type
[2]
1202 atom_material_list
.append(material
)
1204 # Now, we go through all atoms and give them a material. For all atoms ...
1205 for atom
in all_atoms
:
1206 # ... and all materials ...
1207 for material
in atom_material_list
:
1208 # ... select the correct material for the current atom via
1209 # comparison of names ...
1210 if atom
.name
in material
.name
:
1211 # ... and give the atom its material properties.
1212 # However, before we check, if it is a vacancy, because then it
1213 # gets some additional preparation. The vacancy is represented
1214 # by a transparent cube.
1215 if atom
.name
== "Vacancy":
1216 material
.transparency_method
= 'Z_TRANSPARENCY'
1217 material
.alpha
= 1.3
1218 material
.raytrace_transparency
.fresnel
= 1.6
1219 material
.raytrace_transparency
.fresnel_factor
= 1.6
1220 material
.use_transparency
= True
1221 # The atom gets its properties.
1222 atom
.material
= material
1224 # ------------------------------------------------------------------------
1225 # READING DATA OF STICKS
1227 all_sticks
= read_pdb_file_sticks(filepath_pdb
,
1232 # So far, all atoms, sticks and materials have been registered.
1235 # ------------------------------------------------------------------------
1236 # TRANSLATION OF THE STRUCTURE TO THE ORIGIN
1238 # It may happen that the structure in a PDB file already has an offset
1239 # If chosen, the structure is first put into the center of the scene
1240 # (the offset is substracted).
1242 if put_to_center
== True:
1243 sum_vec
= Vector((0.0,0.0,0.0))
1244 # Sum of all atom coordinates
1245 sum_vec
= sum([atom
.location
for atom
in all_atoms
], sum_vec
)
1246 # Then the average is taken
1247 sum_vec
= sum_vec
/ Number_of_total_atoms
1248 # After, for each atom the center of gravity is substracted
1249 for atom
in all_atoms
:
1250 atom
.location
-= sum_vec
1252 # ------------------------------------------------------------------------
1255 # Take all atoms and adjust their radii and scale the distances.
1256 for atom
in all_atoms
:
1257 atom
.location
*= Ball_distance_factor
1259 # ------------------------------------------------------------------------
1260 # DETERMINATION OF SOME GEOMETRIC PROPERTIES
1262 # In the following, some geometric properties of the whole object are
1263 # determined: center, size, etc.
1264 sum_vec
= Vector((0.0,0.0,0.0))
1266 # First the center is determined. All coordinates are summed up ...
1267 sum_vec
= sum([atom
.location
for atom
in all_atoms
], sum_vec
)
1269 # ... and the average is taken. This gives the center of the object.
1270 object_center_vec
= sum_vec
/ Number_of_total_atoms
1272 # Now, we determine the size.The farthest atom from the object center is
1273 # taken as a measure. The size is used to place well the camera and light
1275 object_size_vec
= [atom
.location
- object_center_vec
for atom
in all_atoms
]
1276 object_size
= max(object_size_vec
).length
1278 # ------------------------------------------------------------------------
1281 # Lists of atoms of one type are created. Example:
1282 # draw_all_atoms = [ data_hydrogen,data_carbon,data_nitrogen ]
1283 # data_hydrogen = [["Hydrogen", Material_Hydrogen, Vector((x,y,z)), 109], ...]
1286 # Go through the list which contains all types of atoms. It is the list,
1287 # which has been created on the top during reading the PDB file.
1288 # Example: atom_all_types_list = ["hydrogen", "carbon", ...]
1290 for atom_type
in atom_all_types_list
:
1292 # Don't draw 'TER atoms'.
1293 if atom_type
[0] == "TER":
1296 # This is the draw list, which contains all atoms of one type (e.g.
1297 # all hydrogens) ...
1298 draw_all_atoms_type
= []
1300 # Go through all atoms ...
1301 for atom
in all_atoms
:
1302 # ... select the atoms of the considered type via comparison ...
1303 if atom
.name
== atom_type
[0]:
1304 # ... and append them to the list 'draw_all_atoms_type'.
1305 draw_all_atoms_type
.append([atom
.name
,
1310 # Now append the atom list to the list of all types of atoms
1311 draw_all_atoms
.append(draw_all_atoms_type
)
1313 # ------------------------------------------------------------------------
1316 bpy
.ops
.object.select_all(action
='DESELECT')
1318 # For each list of atoms of ONE type (e.g. Hydrogen)
1319 for draw_all_atoms_type
in draw_all_atoms
:
1321 atom_mesh
= draw_atoms_one_type(draw_all_atoms_type
,
1327 atom_object_list
.append(atom_mesh
)
1329 # ------------------------------------------------------------------------
1330 # DRAWING THE STICKS: cylinders in a dupliverts structure
1332 if use_sticks
== True and use_sticks_type
== '0' and all_sticks
!= []:
1334 sticks
= draw_sticks_dupliverts(all_atoms
,
1335 atom_all_types_list
,
1344 for stick
in sticks
:
1345 atom_object_list
.append(stick
)
1347 # ------------------------------------------------------------------------
1348 # DRAWING THE STICKS: skin and subdivision modifier
1350 if use_sticks
== True and use_sticks_type
== '1' and all_sticks
!= []:
1352 sticks
= draw_sticks_skin(all_atoms
,
1357 sticks_subdiv_render
)
1358 atom_object_list
.append(sticks
)
1360 # ------------------------------------------------------------------------
1361 # DRAWING THE STICKS: normal cylinders
1363 if use_sticks
== True and use_sticks_type
== '2' and all_sticks
!= []:
1365 sticks
= draw_sticks_normal(all_atoms
,
1371 use_sticks_one_object
,
1372 use_sticks_one_object_nr
)
1373 atom_object_list
.append(sticks
)
1375 # ------------------------------------------------------------------------
1376 # CAMERA and LIGHT SOURCES
1378 camera_light_source(use_camera
,
1383 # ------------------------------------------------------------------------
1384 # SELECT ALL LOADED OBJECTS
1385 bpy
.ops
.object.select_all(action
='DESELECT')
1387 for obj
in atom_object_list
:
1390 # activate the last selected object
1392 bpy
.context
.scene
.objects
.active
= obj