1 # SPDX-License-Identifier: GPL-2.0-or-later
6 from mathutils
import Vector
10 # -----------------------------------------------------------------------------
11 # Atom and element data
14 # This is a list that contains some data of all possible elements. The structure
17 # 1, "Hydrogen", "H", [0.0,0.0,1.0], 0.32, 0.32, 0.32 , -1 , 1.54 means
19 # No., name, short name, color, radius (used), radius (covalent), radius (atomic),
21 # charge state 1, radius (ionic) 1, charge state 2, radius (ionic) 2, ... all
22 # charge states for any atom are listed, if existing.
23 # The list is fixed and cannot be changed ... (see below)
26 ( 1, "Hydrogen", "H", ( 1.0, 1.0, 1.0, 1.0), 0.32, 0.32, 0.79 , -1 , 1.54 ),
27 ( 2, "Helium", "He", ( 0.85, 1.0, 1.0, 1.0), 0.93, 0.93, 0.49 ),
28 ( 3, "Lithium", "Li", ( 0.8, 0.50, 1.0, 1.0), 1.23, 1.23, 2.05 , 1 , 0.68 ),
29 ( 4, "Beryllium", "Be", ( 0.76, 1.0, 0.0, 1.0), 0.90, 0.90, 1.40 , 1 , 0.44 , 2 , 0.35 ),
30 ( 5, "Boron", "B", ( 1.0, 0.70, 0.70, 1.0), 0.82, 0.82, 1.17 , 1 , 0.35 , 3 , 0.23 ),
31 ( 6, "Carbon", "C", ( 0.56, 0.56, 0.56, 1.0), 0.77, 0.77, 0.91 , -4 , 2.60 , 4 , 0.16 ),
32 ( 7, "Nitrogen", "N", ( 0.18, 0.31, 0.97, 1.0), 0.75, 0.75, 0.75 , -3 , 1.71 , 1 , 0.25 , 3 , 0.16 , 5 , 0.13 ),
33 ( 8, "Oxygen", "O", ( 1.0, 0.05, 0.05, 1.0), 0.73, 0.73, 0.65 , -2 , 1.32 , -1 , 1.76 , 1 , 0.22 , 6 , 0.09 ),
34 ( 9, "Fluorine", "F", ( 0.56, 0.87, 0.31, 1.0), 0.72, 0.72, 0.57 , -1 , 1.33 , 7 , 0.08 ),
35 (10, "Neon", "Ne", ( 0.70, 0.89, 0.96, 1.0), 0.71, 0.71, 0.51 , 1 , 1.12 ),
36 (11, "Sodium", "Na", ( 0.67, 0.36, 0.94, 1.0), 1.54, 1.54, 2.23 , 1 , 0.97 ),
37 (12, "Magnesium", "Mg", ( 0.54, 1.0, 0.0, 1.0), 1.36, 1.36, 1.72 , 1 , 0.82 , 2 , 0.66 ),
38 (13, "Aluminium", "Al", ( 0.74, 0.65, 0.65, 1.0), 1.18, 1.18, 1.82 , 3 , 0.51 ),
39 (14, "Silicon", "Si", ( 0.94, 0.78, 0.62, 1.0), 1.11, 1.11, 1.46 , -4 , 2.71 , -1 , 3.84 , 1 , 0.65 , 4 , 0.42 ),
40 (15, "Phosphorus", "P", ( 1.0, 0.50, 0.0, 1.0), 1.06, 1.06, 1.23 , -3 , 2.12 , 3 , 0.44 , 5 , 0.35 ),
41 (16, "Sulfur", "S", ( 1.0, 1.0, 0.18, 1.0), 1.02, 1.02, 1.09 , -2 , 1.84 , 2 , 2.19 , 4 , 0.37 , 6 , 0.30 ),
42 (17, "Chlorine", "Cl", ( 0.12, 0.94, 0.12, 1.0), 0.99, 0.99, 0.97 , -1 , 1.81 , 5 , 0.34 , 7 , 0.27 ),
43 (18, "Argon", "Ar", ( 0.50, 0.81, 0.89, 1.0), 0.98, 0.98, 0.88 , 1 , 1.54 ),
44 (19, "Potassium", "K", ( 0.56, 0.25, 0.83, 1.0), 2.03, 2.03, 2.77 , 1 , 0.81 ),
45 (20, "Calcium", "Ca", ( 0.23, 1.0, 0.0, 1.0), 1.74, 1.74, 2.23 , 1 , 1.18 , 2 , 0.99 ),
46 (21, "Scandium", "Sc", ( 0.90, 0.90, 0.90, 1.0), 1.44, 1.44, 2.09 , 3 , 0.73 ),
47 (22, "Titanium", "Ti", ( 0.74, 0.76, 0.78, 1.0), 1.32, 1.32, 2.00 , 1 , 0.96 , 2 , 0.94 , 3 , 0.76 , 4 , 0.68 ),
48 (23, "Vanadium", "V", ( 0.65, 0.65, 0.67, 1.0), 1.22, 1.22, 1.92 , 2 , 0.88 , 3 , 0.74 , 4 , 0.63 , 5 , 0.59 ),
49 (24, "Chromium", "Cr", ( 0.54, 0.6, 0.78, 1.0), 1.18, 1.18, 1.85 , 1 , 0.81 , 2 , 0.89 , 3 , 0.63 , 6 , 0.52 ),
50 (25, "Manganese", "Mn", ( 0.61, 0.47, 0.78, 1.0), 1.17, 1.17, 1.79 , 2 , 0.80 , 3 , 0.66 , 4 , 0.60 , 7 , 0.46 ),
51 (26, "Iron", "Fe", ( 0.87, 0.4, 0.2, 1.0), 1.17, 1.17, 1.72 , 2 , 0.74 , 3 , 0.64 ),
52 (27, "Cobalt", "Co", ( 0.94, 0.56, 0.62, 1.0), 1.16, 1.16, 1.67 , 2 , 0.72 , 3 , 0.63 ),
53 (28, "Nickel", "Ni", ( 0.31, 0.81, 0.31, 1.0), 1.15, 1.15, 1.62 , 2 , 0.69 ),
54 (29, "Copper", "Cu", ( 0.78, 0.50, 0.2, 1.0), 1.17, 1.17, 1.57 , 1 , 0.96 , 2 , 0.72 ),
55 (30, "Zinc", "Zn", ( 0.49, 0.50, 0.69, 1.0), 1.25, 1.25, 1.53 , 1 , 0.88 , 2 , 0.74 ),
56 (31, "Gallium", "Ga", ( 0.76, 0.56, 0.56, 1.0), 1.26, 1.26, 1.81 , 1 , 0.81 , 3 , 0.62 ),
57 (32, "Germanium", "Ge", ( 0.4, 0.56, 0.56, 1.0), 1.22, 1.22, 1.52 , -4 , 2.72 , 2 , 0.73 , 4 , 0.53 ),
58 (33, "Arsenic", "As", ( 0.74, 0.50, 0.89, 1.0), 1.20, 1.20, 1.33 , -3 , 2.22 , 3 , 0.58 , 5 , 0.46 ),
59 (34, "Selenium", "Se", ( 1.0, 0.63, 0.0, 1.0), 1.16, 1.16, 1.22 , -2 , 1.91 , -1 , 2.32 , 1 , 0.66 , 4 , 0.50 , 6 , 0.42 ),
60 (35, "Bromine", "Br", ( 0.65, 0.16, 0.16, 1.0), 1.14, 1.14, 1.12 , -1 , 1.96 , 5 , 0.47 , 7 , 0.39 ),
61 (36, "Krypton", "Kr", ( 0.36, 0.72, 0.81, 1.0), 1.31, 1.31, 1.24 ),
62 (37, "Rubidium", "Rb", ( 0.43, 0.18, 0.69, 1.0), 2.16, 2.16, 2.98 , 1 , 1.47 ),
63 (38, "Strontium", "Sr", ( 0.0, 1.0, 0.0, 1.0), 1.91, 1.91, 2.45 , 2 , 1.12 ),
64 (39, "Yttrium", "Y", ( 0.58, 1.0, 1.0, 1.0), 1.62, 1.62, 2.27 , 3 , 0.89 ),
65 (40, "Zirconium", "Zr", ( 0.58, 0.87, 0.87, 1.0), 1.45, 1.45, 2.16 , 1 , 1.09 , 4 , 0.79 ),
66 (41, "Niobium", "Nb", ( 0.45, 0.76, 0.78, 1.0), 1.34, 1.34, 2.08 , 1 , 1.00 , 4 , 0.74 , 5 , 0.69 ),
67 (42, "Molybdenum", "Mo", ( 0.32, 0.70, 0.70, 1.0), 1.30, 1.30, 2.01 , 1 , 0.93 , 4 , 0.70 , 6 , 0.62 ),
68 (43, "Technetium", "Tc", ( 0.23, 0.61, 0.61, 1.0), 1.27, 1.27, 1.95 , 7 , 0.97 ),
69 (44, "Ruthenium", "Ru", ( 0.14, 0.56, 0.56, 1.0), 1.25, 1.25, 1.89 , 4 , 0.67 ),
70 (45, "Rhodium", "Rh", ( 0.03, 0.49, 0.54, 1.0), 1.25, 1.25, 1.83 , 3 , 0.68 ),
71 (46, "Palladium", "Pd", ( 0.0, 0.41, 0.52, 1.0), 1.28, 1.28, 1.79 , 2 , 0.80 , 4 , 0.65 ),
72 (47, "Silver", "Ag", ( 0.75, 0.75, 0.75, 1.0), 1.34, 1.34, 1.75 , 1 , 1.26 , 2 , 0.89 ),
73 (48, "Cadmium", "Cd", ( 1.0, 0.85, 0.56, 1.0), 1.48, 1.48, 1.71 , 1 , 1.14 , 2 , 0.97 ),
74 (49, "Indium", "In", ( 0.65, 0.45, 0.45, 1.0), 1.44, 1.44, 2.00 , 3 , 0.81 ),
75 (50, "Tin", "Sn", ( 0.4, 0.50, 0.50, 1.0), 1.41, 1.41, 1.72 , -4 , 2.94 , -1 , 3.70 , 2 , 0.93 , 4 , 0.71 ),
76 (51, "Antimony", "Sb", ( 0.61, 0.38, 0.70, 1.0), 1.40, 1.40, 1.53 , -3 , 2.45 , 3 , 0.76 , 5 , 0.62 ),
77 (52, "Tellurium", "Te", ( 0.83, 0.47, 0.0, 1.0), 1.36, 1.36, 1.42 , -2 , 2.11 , -1 , 2.50 , 1 , 0.82 , 4 , 0.70 , 6 , 0.56 ),
78 (53, "Iodine", "I", ( 0.58, 0.0, 0.58, 1.0), 1.33, 1.33, 1.32 , -1 , 2.20 , 5 , 0.62 , 7 , 0.50 ),
79 (54, "Xenon", "Xe", ( 0.25, 0.61, 0.69, 1.0), 1.31, 1.31, 1.24 ),
80 (55, "Caesium", "Cs", ( 0.34, 0.09, 0.56, 1.0), 2.35, 2.35, 3.35 , 1 , 1.67 ),
81 (56, "Barium", "Ba", ( 0.0, 0.78, 0.0, 1.0), 1.98, 1.98, 2.78 , 1 , 1.53 , 2 , 1.34 ),
82 (57, "Lanthanum", "La", ( 0.43, 0.83, 1.0, 1.0), 1.69, 1.69, 2.74 , 1 , 1.39 , 3 , 1.06 ),
83 (58, "Cerium", "Ce", ( 1.0, 1.0, 0.78, 1.0), 1.65, 1.65, 2.70 , 1 , 1.27 , 3 , 1.03 , 4 , 0.92 ),
84 (59, "Praseodymium", "Pr", ( 0.85, 1.0, 0.78, 1.0), 1.65, 1.65, 2.67 , 3 , 1.01 , 4 , 0.90 ),
85 (60, "Neodymium", "Nd", ( 0.78, 1.0, 0.78, 1.0), 1.64, 1.64, 2.64 , 3 , 0.99 ),
86 (61, "Promethium", "Pm", ( 0.63, 1.0, 0.78, 1.0), 1.63, 1.63, 2.62 , 3 , 0.97 ),
87 (62, "Samarium", "Sm", ( 0.56, 1.0, 0.78, 1.0), 1.62, 1.62, 2.59 , 3 , 0.96 ),
88 (63, "Europium", "Eu", ( 0.38, 1.0, 0.78, 1.0), 1.85, 1.85, 2.56 , 2 , 1.09 , 3 , 0.95 ),
89 (64, "Gadolinium", "Gd", ( 0.27, 1.0, 0.78, 1.0), 1.61, 1.61, 2.54 , 3 , 0.93 ),
90 (65, "Terbium", "Tb", ( 0.18, 1.0, 0.78, 1.0), 1.59, 1.59, 2.51 , 3 , 0.92 , 4 , 0.84 ),
91 (66, "Dysprosium", "Dy", ( 0.12, 1.0, 0.78, 1.0), 1.59, 1.59, 2.49 , 3 , 0.90 ),
92 (67, "Holmium", "Ho", ( 0.0, 1.0, 0.61, 1.0), 1.58, 1.58, 2.47 , 3 , 0.89 ),
93 (68, "Erbium", "Er", ( 0.0, 0.90, 0.45, 1.0), 1.57, 1.57, 2.45 , 3 , 0.88 ),
94 (69, "Thulium", "Tm", ( 0.0, 0.83, 0.32, 1.0), 1.56, 1.56, 2.42 , 3 , 0.87 ),
95 (70, "Ytterbium", "Yb", ( 0.0, 0.74, 0.21, 1.0), 1.74, 1.74, 2.40 , 2 , 0.93 , 3 , 0.85 ),
96 (71, "Lutetium", "Lu", ( 0.0, 0.67, 0.14, 1.0), 1.56, 1.56, 2.25 , 3 , 0.85 ),
97 (72, "Hafnium", "Hf", ( 0.30, 0.76, 1.0, 1.0), 1.44, 1.44, 2.16 , 4 , 0.78 ),
98 (73, "Tantalum", "Ta", ( 0.30, 0.65, 1.0, 1.0), 1.34, 1.34, 2.09 , 5 , 0.68 ),
99 (74, "Tungsten", "W", ( 0.12, 0.58, 0.83, 1.0), 1.30, 1.30, 2.02 , 4 , 0.70 , 6 , 0.62 ),
100 (75, "Rhenium", "Re", ( 0.14, 0.49, 0.67, 1.0), 1.28, 1.28, 1.97 , 4 , 0.72 , 7 , 0.56 ),
101 (76, "Osmium", "Os", ( 0.14, 0.4, 0.58, 1.0), 1.26, 1.26, 1.92 , 4 , 0.88 , 6 , 0.69 ),
102 (77, "Iridium", "Ir", ( 0.09, 0.32, 0.52, 1.0), 1.27, 1.27, 1.87 , 4 , 0.68 ),
103 (78, "Platinum", "Pt", ( 0.81, 0.81, 0.87, 1.0), 1.30, 1.30, 1.83 , 2 , 0.80 , 4 , 0.65 ),
104 (79, "Gold", "Au", ( 1.0, 0.81, 0.13, 1.0), 1.34, 1.34, 1.79 , 1 , 1.37 , 3 , 0.85 ),
105 (80, "Mercury", "Hg", ( 0.72, 0.72, 0.81, 1.0), 1.49, 1.49, 1.76 , 1 , 1.27 , 2 , 1.10 ),
106 (81, "Thallium", "Tl", ( 0.65, 0.32, 0.30, 1.0), 1.48, 1.48, 2.08 , 1 , 1.47 , 3 , 0.95 ),
107 (82, "Lead", "Pb", ( 0.34, 0.34, 0.38, 1.0), 1.47, 1.47, 1.81 , 2 , 1.20 , 4 , 0.84 ),
108 (83, "Bismuth", "Bi", ( 0.61, 0.30, 0.70, 1.0), 1.46, 1.46, 1.63 , 1 , 0.98 , 3 , 0.96 , 5 , 0.74 ),
109 (84, "Polonium", "Po", ( 0.67, 0.36, 0.0, 1.0), 1.46, 1.46, 1.53 , 6 , 0.67 ),
110 (85, "Astatine", "At", ( 0.45, 0.30, 0.27, 1.0), 1.45, 1.45, 1.43 , -3 , 2.22 , 3 , 0.85 , 5 , 0.46 ),
111 (86, "Radon", "Rn", ( 0.25, 0.50, 0.58, 1.0), 1.00, 1.00, 1.34 ),
112 (87, "Francium", "Fr", ( 0.25, 0.0, 0.4, 1.0), 1.00, 1.00, 1.00 , 1 , 1.80 ),
113 (88, "Radium", "Ra", ( 0.0, 0.49, 0.0, 1.0), 1.00, 1.00, 1.00 , 2 , 1.43 ),
114 (89, "Actinium", "Ac", ( 0.43, 0.67, 0.98, 1.0), 1.00, 1.00, 1.00 , 3 , 1.18 ),
115 (90, "Thorium", "Th", ( 0.0, 0.72, 1.0, 1.0), 1.65, 1.65, 1.00 , 4 , 1.02 ),
116 (91, "Protactinium", "Pa", ( 0.0, 0.63, 1.0, 1.0), 1.00, 1.00, 1.00 , 3 , 1.13 , 4 , 0.98 , 5 , 0.89 ),
117 (92, "Uranium", "U", ( 0.0, 0.56, 1.0, 1.0), 1.42, 1.42, 1.00 , 4 , 0.97 , 6 , 0.80 ),
118 (93, "Neptunium", "Np", ( 0.0, 0.50, 1.0, 1.0), 1.00, 1.00, 1.00 , 3 , 1.10 , 4 , 0.95 , 7 , 0.71 ),
119 (94, "Plutonium", "Pu", ( 0.0, 0.41, 1.0, 1.0), 1.00, 1.00, 1.00 , 3 , 1.08 , 4 , 0.93 ),
120 (95, "Americium", "Am", ( 0.32, 0.36, 0.94, 1.0), 1.00, 1.00, 1.00 , 3 , 1.07 , 4 , 0.92 ),
121 (96, "Curium", "Cm", ( 0.47, 0.36, 0.89, 1.0), 1.00, 1.00, 1.00 ),
122 (97, "Berkelium", "Bk", ( 0.54, 0.30, 0.89, 1.0), 1.00, 1.00, 1.00 ),
123 (98, "Californium", "Cf", ( 0.63, 0.21, 0.83, 1.0), 1.00, 1.00, 1.00 ),
124 (99, "Einsteinium", "Es", ( 0.70, 0.12, 0.83, 1.0), 1.00, 1.00, 1.00 ),
125 (100, "Fermium", "Fm", ( 0.70, 0.12, 0.72, 1.0), 1.00, 1.00, 1.00 ),
126 (101, "Mendelevium", "Md", ( 0.70, 0.05, 0.65, 1.0), 1.00, 1.00, 1.00 ),
127 (102, "Nobelium", "No", ( 0.74, 0.05, 0.52, 1.0), 1.00, 1.00, 1.00 ),
128 (103, "Lawrencium", "Lr", ( 0.78, 0.0, 0.4, 1.0), 1.00, 1.00, 1.00 ),
129 (104, "Vacancy", "Vac", ( 0.5, 0.5, 0.5, 1.0), 1.00, 1.00, 1.00),
130 (105, "Default", "Default", ( 1.0, 1.0, 1.0, 1.0), 1.00, 1.00, 1.00),
131 (106, "Stick", "Stick", ( 0.5, 0.5, 0.5, 1.0), 1.00, 1.00, 1.00),
134 # The list 'ELEMENTS' contains all data of the elements and will be used during
135 # runtime. The list will be initialized with the fixed
136 # data from above via the class below (ElementProp). One fixed list (above),
137 # which cannot be changed, and a list of classes with same data (ELEMENTS) exist.
138 # The list 'ELEMENTS' can be modified by e.g. loading a separate custom
143 # This is the class, which stores the properties for one element.
144 class ElementProp(object):
145 __slots__
= ('number',
164 self
.short_name
= short_name
167 self
.radii_ionic
= radii_ionic
168 self
.mat_P_BSDF
= mat_P_BSDF
169 self
.mat_Eevee
= mat_Eevee
172 class PBSDFProp(object):
173 __slots__
= ('Subsurface_method',
183 'Anisotropic_rotation',
205 Anisotropic_rotation
,
216 self
.Subsurface_method
= Subsurface_method
217 self
.Distribution
= Distribution
218 self
.Subsurface
= Subsurface
219 self
.Subsurface_color
= Subsurface_color
220 self
.Subsurface_radius
= Subsurface_radius
221 self
.Metallic
= Metallic
222 self
.Specular
= Specular
223 self
.Specular_tilt
= Specular_tilt
224 self
.Roughness
= Roughness
225 self
.Anisotropic
= Anisotropic
226 self
.Anisotropic_rotation
= Anisotropic_rotation
228 self
.Sheen_tint
= Sheen_tint
229 self
.Clearcoat
= Clearcoat
230 self
.Clearcoat_rough
= Clearcoat_rough
233 self
.Trans_rough
= Trans_rough
234 self
.Emission
= Emission
235 self
.Emission_strength
= Emission_strength
239 class EeveeProp(object):
240 __slots__
= ('use_backface',
244 'use_screen_refraction',
246 'use_sss_translucency',
253 use_screen_refraction
,
255 use_sss_translucency
,
257 self
.use_backface
= use_backface
258 self
.blend_method
= blend_method
259 self
.shadow_method
= shadow_method
260 self
.clip_threshold
= clip_threshold
261 self
.use_screen_refraction
= use_screen_refraction
262 self
.refraction_depth
= refraction_depth
263 self
.use_sss_translucency
= use_sss_translucency
264 self
.pass_index
= pass_index
267 # This function measures the distance between two selected objects.
271 if bpy
.context
.mode
== 'EDIT_MESH':
273 atom
= bpy
.context
.edit_object
274 bm
= bmesh
.from_edit_mesh(atom
.data
)
279 locations
.append(atom
.matrix_world
@ v
.co
)
281 if len(locations
) > 1:
282 location1
= locations
[0]
283 location2
= locations
[1]
286 # In the 'OBJECT' mode
289 if len(bpy
.context
.selected_objects
) > 1:
290 location1
= bpy
.context
.selected_objects
[0].location
291 location2
= bpy
.context
.selected_objects
[1].location
295 dv
= location2
- location1
296 dist
= str(dv
.length
)
297 pos
= str.find(dist
, ".")
304 def choose_objects(action_type
,
311 # Note all selected objects first.
312 change_objects_all
= []
313 for atom
in bpy
.context
.selected_objects
:
314 change_objects_all
.append(atom
)
316 # This is very important now: If there are dupliverts structures, note
317 # only the parents and NOT the children! Otherwise the double work is
318 # done or the system can even crash if objects are deleted. - The
319 # chidlren are accessed anyways (see below).
321 for atom
in change_objects_all
:
322 if atom
.parent
!= None:
324 for atom2
in change_objects
:
325 if atom2
== atom
.parent
:
328 change_objects
.append(atom
)
330 change_objects
.append(atom
)
332 # And now, consider all objects, which are in the list 'change_objects'.
333 for atom
in change_objects
:
334 if len(atom
.children
) != 0:
335 for atom_child
in atom
.children
:
336 if atom_child
.type in {'SURFACE', 'MESH', 'META'}:
337 modify_objects(action_type
,
345 if atom
.type in {'SURFACE', 'MESH', 'META'}:
346 modify_objects(action_type
,
355 # Modifying the radius of a selected atom or stick
356 def modify_objects(action_type
,
364 # Modify atom radius (in pm)
365 if action_type
== "ATOM_RADIUS_PM" and "STICK" not in atom
.name
.upper():
366 if radius_pm
[0] in atom
.name
:
367 atom
.scale
= (radius_pm
[1]/100,) * 3
369 # Modify atom radius (all selected)
370 if action_type
== "ATOM_RADIUS_ALL" and "STICK" not in atom
.name
.upper():
371 atom
.scale
*= radius_all
373 # Modify atom radius (type, van der Waals, atomic or ionic)
374 if action_type
== "ATOM_RADIUS_TYPE" and "STICK" not in atom
.name
.upper():
375 for element
in ELEMENTS
:
376 if element
.name
in atom
.name
:
378 if radius_type
== '3':
379 charge_states
= element
.radii_ionic
[::2]
380 charge_radii
= element
.radii_ionic
[1::2]
381 charge_state_chosen
= int(radius_type_ionic
) - 4
383 find
= (lambda searchList
, elem
:
384 [[i
for i
, x
in enumerate(searchList
) if x
== e
]
386 index
= find(charge_states
,[charge_state_chosen
])[0]
388 # Is there a charge state?
390 atom
.scale
= (charge_radii
[index
[0]],) * 3
392 # For atomic and van der Waals radii.
394 atom
.scale
= (element
.radii
[int(radius_type
)],) * 3
397 if (action_type
== "STICKS_RADIUS_ALL" and 'STICK' in atom
.name
.upper() and
398 ('CUP' in atom
.name
.upper() or
399 'CYLINDER' in atom
.name
.upper())):
401 # For dupliverts structures only: Make the cylinder or cup visible
402 # first, otherwise one cannot go into EDIT mode. Note that 'atom' here
403 # is in fact a 'stick' (cylinder or cup).
404 # First, identify if it is a normal cylinder object or a dupliverts
405 # structure. The identifier for a dupliverts structure is the parent's
406 # name, which includes "_sticks_mesh"
407 if "_sticks_mesh" in atom
.parent
.name
:
410 bpy
.context
.view_layer
.objects
.active
= atom
411 bpy
.ops
.object.mode_set(mode
='EDIT', toggle
=False)
412 bm
= bmesh
.from_edit_mesh(atom
.data
)
416 locations
.append(v
.co
)
418 center
= Vector((0.0,0.0,0.0))
419 center
= sum([location
for location
in locations
], center
)/len(locations
)
421 radius
= sum([(loc
[0]-center
[0])**2+(loc
[1]-center
[1])**2
422 for loc
in locations
], 0)
423 radius_new
= radius
* sticks_all
426 v
.co
[0] = ((v
.co
[0] - center
[0]) / radius
) * radius_new
+ center
[0]
427 v
.co
[1] = ((v
.co
[1] - center
[1]) / radius
) * radius_new
+ center
[1]
429 bpy
.ops
.object.mode_set(mode
='OBJECT', toggle
=False)
430 # Hide again the representative stick (cylinder or cup) if it is a
431 # dupliverts structure.
432 if "_sticks_mesh" in atom
.parent
.name
:
435 bpy
.context
.view_layer
.objects
.active
= None
437 # Change the atom objects
438 if action_type
== "ATOM_REPLACE_OBJ" and "STICK" not in atom
.name
.upper():
440 scn
= bpy
.context
.scene
.atom_blend
442 material
= atom
.active_material
443 new_material
= draw_obj_material(scn
.replace_objs_material
, material
)
445 # Special object (like halo, etc.)
446 if scn
.replace_objs_special
!= '0':
447 atom
= draw_obj_special(scn
.replace_objs_special
, atom
)
448 # Standard geometrical objects
450 # If the atom shape shall not be changed, then:
451 if scn
.replace_objs
== '0':
452 atom
.active_material
= new_material
453 # If the atom shape shall change, then:
455 atom
= draw_obj(scn
.replace_objs
, atom
, new_material
)
457 # Find the sticks, if present.
458 sticks_cylinder
, sticks_cup
= find_sticks_of_atom(atom
)
461 if sticks_cylinder
!= None and sticks_cup
!= None:
462 sticks_cylinder
.active_material
= new_material
463 sticks_cup
.active_material
= new_material
464 if sticks_cylinder
!= None and sticks_cup
== None:
466 if type(sticks_cylinder
) == list:
467 for stick
in sticks_cylinder
:
468 stick
.active_material
= new_material
471 sticks_cylinder
.active_material
= new_material
473 # If the atom is the representative ball of a dupliverts structure,
474 # then make it invisible.
475 if atom
.parent
!= None:
478 # Default shape and colors for atoms
479 if action_type
== "ATOM_DEFAULT_OBJ" and "STICK" not in atom
.name
.upper():
481 scn
= bpy
.context
.scene
.atom_blend
483 # We first obtain the element form the list of elements.
484 for element
in ELEMENTS
:
485 if element
.name
in atom
.name
:
488 # Create now a new material with normal properties. Note that the
489 # 'normal material' initially used during the import could have been
490 # deleted by the user. This is why we create a new one.
491 if "vacancy" in atom
.name
.lower():
492 new_material
= draw_obj_material('2', atom
.active_material
)
494 new_material
= draw_obj_material('1', atom
.active_material
)
495 # Assign now the correct color.
496 mat_P_BSDF
= next(n
for n
in new_material
.node_tree
.nodes
497 if n
.type == "BSDF_PRINCIPLED")
498 mat_P_BSDF
.inputs
['Base Color'].default_value
= element
.color
499 new_material
.name
= element
.name
+ "_normal"
501 # Create a new atom because the current atom might have any kind
502 # of shape. For this, we use a definition from below since it also
503 # deletes the old atom.
504 if "vacancy" in atom
.name
.lower():
505 new_atom
= draw_obj('2', atom
, new_material
)
507 new_atom
= draw_obj('1b', atom
, new_material
)
509 # Now assign the material properties, name and size.
510 new_atom
.active_material
= new_material
511 new_atom
.name
= element
.name
+ "_ball"
512 new_atom
.scale
= (element
.radii
[0],) * 3
514 # Find the sticks, if present.
515 sticks_cylinder
, sticks_cup
= find_sticks_of_atom(new_atom
)
518 if sticks_cylinder
!= None and sticks_cup
!= None:
519 sticks_cylinder
.active_material
= new_material
520 sticks_cup
.active_material
= new_material
521 if sticks_cylinder
!= None and sticks_cup
== None:
523 if type(sticks_cylinder
) == list:
524 for stick
in sticks_cylinder
:
525 stick
.active_material
= new_material
528 sticks_cylinder
.active_material
= new_material
531 # Separating atoms from a dupliverts structure.
532 def separate_atoms(scn
):
535 mesh
= bpy
.context
.edit_object
537 # Do nothing if it is not a dupliverts structure.
538 if not mesh
.instance_type
== "VERTS":
541 # This is the name of the mesh
542 mesh_name
= mesh
.name
544 coll
= mesh
.users_collection
[0]
546 # Get the coordinates of the selected vertices (atoms)
547 bm
= bmesh
.from_edit_mesh(mesh
.data
)
551 locations
.append(mesh
.matrix_world
@ v
.co
)
556 # Delete already the selected vertices
557 bpy
.ops
.mesh
.delete(type='VERT')
559 # Find the representative ball within the collection.
560 for obj
in coll
.objects
:
561 if obj
.parent
!= None:
562 if obj
.parent
.name
== mesh_name
:
565 # Create balls and put them at the places where the vertices (atoms) have
567 for location
in locations
:
568 obj_dupli
= obj
.copy()
569 obj_dupli
.data
= obj
.data
.copy()
570 obj_dupli
.parent
= None
571 coll
.objects
.link(obj_dupli
)
572 obj_dupli
.location
= location
573 obj_dupli
.name
= obj
.name
+ "_sep"
574 # Do not hide the object!
575 obj_dupli
.hide_set(False)
577 bpy
.ops
.object.mode_set(mode
='OBJECT', toggle
=False)
578 bpy
.context
.view_layer
.objects
.active
= mesh
581 # Prepare a new material
582 def draw_obj_material(material_type
, material
):
584 mat_P_BSDF_default
= next(n
for n
in material
.node_tree
.nodes
585 if n
.type == "BSDF_PRINCIPLED")
586 default_color
= mat_P_BSDF_default
.inputs
['Base Color'].default_value
588 if material_type
== '0': # Unchanged
589 material_new
= material
590 if material_type
== '1': # Normal
591 # We create again the 'normal' material. Why? It's because the old
592 # one could have been deleted by the user during the course of the
593 # user's work in Blender ... .
594 material_new
= bpy
.data
.materials
.new(material
.name
+ "_normal")
595 material_new
.use_nodes
= True
596 mat_P_BSDF
= next(n
for n
in material_new
.node_tree
.nodes
597 if n
.type == "BSDF_PRINCIPLED")
598 mat_P_BSDF
.inputs
['Base Color'].default_value
= default_color
599 mat_P_BSDF
.inputs
['Metallic'].default_value
= 0.0
600 mat_P_BSDF
.inputs
['Specular'].default_value
= 0.5
601 mat_P_BSDF
.inputs
['Roughness'].default_value
= 0.5
602 mat_P_BSDF
.inputs
['Clearcoat Roughness'].default_value
= 0.03
603 mat_P_BSDF
.inputs
['IOR'].default_value
= 1.45
604 mat_P_BSDF
.inputs
['Transmission'].default_value
= 0.0
605 mat_P_BSDF
.inputs
['Transmission Roughness'].default_value
= 0.0
606 mat_P_BSDF
.inputs
['Alpha'].default_value
= 1.0
607 # Some additional stuff for eevee.
608 material_new
.blend_method
= 'OPAQUE'
609 material_new
.shadow_method
= 'OPAQUE'
610 if material_type
== '2': # Transparent
611 material_new
= bpy
.data
.materials
.new(material
.name
+ "_transparent")
612 material_new
.use_nodes
= True
613 mat_P_BSDF
= next(n
for n
in material_new
.node_tree
.nodes
614 if n
.type == "BSDF_PRINCIPLED")
615 mat_P_BSDF
.inputs
['Base Color'].default_value
= default_color
616 mat_P_BSDF
.inputs
['Metallic'].default_value
= 0.0
617 mat_P_BSDF
.inputs
['Specular'].default_value
= 0.15
618 mat_P_BSDF
.inputs
['Roughness'].default_value
= 0.2
619 mat_P_BSDF
.inputs
['Clearcoat Roughness'].default_value
= 0.37
620 mat_P_BSDF
.inputs
['IOR'].default_value
= 1.45
621 mat_P_BSDF
.inputs
['Transmission'].default_value
= 0.8
622 mat_P_BSDF
.inputs
['Transmission Roughness'].default_value
= 0.0
623 mat_P_BSDF
.inputs
['Alpha'].default_value
= 0.4
624 # Some additional stuff for eevee.
625 material_new
.blend_method
= 'HASHED'
626 material_new
.shadow_method
= 'HASHED'
627 material_new
.use_backface_culling
= False
628 if material_type
== '3': # Reflecting
629 material_new
= bpy
.data
.materials
.new(material
.name
+ "_reflecting")
630 material_new
.use_nodes
= True
631 mat_P_BSDF
= next(n
for n
in material_new
.node_tree
.nodes
632 if n
.type == "BSDF_PRINCIPLED")
633 mat_P_BSDF
.inputs
['Base Color'].default_value
= default_color
634 mat_P_BSDF
.inputs
['Metallic'].default_value
= 0.7
635 mat_P_BSDF
.inputs
['Specular'].default_value
= 0.15
636 mat_P_BSDF
.inputs
['Roughness'].default_value
= 0.1
637 mat_P_BSDF
.inputs
['Clearcoat Roughness'].default_value
= 0.5
638 mat_P_BSDF
.inputs
['IOR'].default_value
= 0.8
639 mat_P_BSDF
.inputs
['Transmission'].default_value
= 0.0
640 mat_P_BSDF
.inputs
['Transmission Roughness'].default_value
= 0.0
641 mat_P_BSDF
.inputs
['Alpha'].default_value
= 1.0
642 # Some additional stuff for eevee.
643 material_new
.blend_method
= 'OPAQUE'
644 material_new
.shadow_method
= 'OPAQUE'
645 if material_type
== '4': # Transparent + reflecting
646 material_new
= bpy
.data
.materials
.new(material
.name
+ "_trans+refl")
647 material_new
.use_nodes
= True
648 mat_P_BSDF
= next(n
for n
in material_new
.node_tree
.nodes
649 if n
.type == "BSDF_PRINCIPLED")
650 mat_P_BSDF
.inputs
['Base Color'].default_value
= default_color
651 mat_P_BSDF
.inputs
['Metallic'].default_value
= 0.5
652 mat_P_BSDF
.inputs
['Specular'].default_value
= 0.15
653 mat_P_BSDF
.inputs
['Roughness'].default_value
= 0.05
654 mat_P_BSDF
.inputs
['Clearcoat Roughness'].default_value
= 0.37
655 mat_P_BSDF
.inputs
['IOR'].default_value
= 1.45
656 mat_P_BSDF
.inputs
['Transmission'].default_value
= 0.6
657 mat_P_BSDF
.inputs
['Transmission Roughness'].default_value
= 0.0
658 mat_P_BSDF
.inputs
['Alpha'].default_value
= 0.6
659 # Some additional stuff for eevee.
660 material_new
.blend_method
= 'HASHED'
661 material_new
.shadow_method
= 'HASHED'
662 material_new
.use_backface_culling
= False
664 # Always, when the material is changed, a new name is created. Note that
665 # this makes sense: Imagine, an other object uses the same material as the
666 # selected one. After changing the material of the selected object the old
667 # material should certainly not change and remain the same.
668 if material_type
in {'1','2','3','4'}:
669 if "_repl" in material
.name
:
670 pos
= material
.name
.rfind("_repl")
671 if material
.name
[pos
+5:].isdigit():
672 counter
= int(material
.name
[pos
+5:])
673 material_new
.name
= material
.name
[:pos
]+"_repl"+str(counter
+1)
675 material_new
.name
= material
.name
+"_repl1"
677 material_new
.name
= material
.name
+"_repl1"
678 material_new
.diffuse_color
= material
.diffuse_color
683 # Get the collection of an object.
684 def get_collection_object(obj
):
686 coll_all
= obj
.users_collection
687 if len(coll_all
) > 0:
690 coll
= bpy
.context
.scene
.collection
695 # Find the sticks of an atom.
696 def find_sticks_of_atom(atom
):
698 # Initialization of the stick objects 'cylinder' and 'cup'.
699 sticks_cylinder
= None
702 # This is for dupliverts structures.
703 if atom
.parent
!= None:
708 # Get a list of all scenes.
709 cols_scene
= [c
for c
in D
.collections
if C
.scene
.user_of_id(c
)]
711 # This is the collection where the atom is inside.
712 col_atom
= atom
.parent
.users_collection
[0]
714 # Get the parent collection of the latter collection.
715 col_parent
= [c
for c
in cols_scene
if c
.user_of_id(col_atom
)][0]
717 # Get **all** children collections inside this parent collection.
718 parent_childrens
= col_parent
.children_recursive
720 # This is for dupliverts stick structures now: for each child
722 for child
in parent_childrens
:
723 # It should not have the name of the atom collection.
724 if child
.name
!= col_atom
.name
:
725 # If the sticks are inside then ...
726 if "sticks" in child
.name
:
727 # For all objects do ...
728 for obj
in child
.objects
:
729 # If the stick objects are inside then note them.
730 if "sticks_cylinder" in obj
.name
:
731 sticks_cylinder
= obj
732 if "sticks_cup" in obj
.name
:
735 # No dupliverts stick structures found? Then lets search for
736 # 'normal' and 'skin' sticks. Such sticks are in the collection
737 # 'Sticks' of the atomic structure.
738 if sticks_cylinder
== None and sticks_cup
== None:
740 # Get the grandparent collection of the parent collection.
741 col_grandparent
= [c
for c
in cols_scene
if c
.user_of_id(col_parent
)][0]
744 list_objs
= col_grandparent
.objects
745 for obj
in list_objs
:
746 if "Sticks" in obj
.name
:
747 sticks_cylinder
= obj
751 if sticks_cylinder
== None:
752 # For each child collection do:
753 for child
in col_grandparent
.children_recursive
:
754 # If the sticks are inside then ...
755 if "Sticks_cylinders" in child
.name
:
757 for obj
in child
.objects
:
758 sticks_cylinder
.append(obj
)
761 # Return the stick objects 'cylinder' and 'cup'.
763 # Dupliverts sticks => sticks_cylinder = 1, sticks_cup = 1
764 # Skin sticks => sticks_cylinder = 1, sticks_cup = None
765 # Normal sticks => sticks_cylinder = [n], sticks_cup = None
766 return sticks_cylinder
, sticks_cup
769 # Draw an object (e.g. cube, sphere, cylinder, ...)
770 def draw_obj(atom_shape
, atom
, new_material
):
773 if atom_shape
== '0':
776 if atom_shape
== '1a': #Sphere mesh
777 bpy
.ops
.mesh
.primitive_uv_sphere_add(
782 enter_editmode
=False,
783 location
=atom
.location
,
785 if atom_shape
== '1b': #Sphere NURBS
786 bpy
.ops
.surface
.primitive_nurbs_surface_sphere_add(
788 enter_editmode
=False,
789 location
=atom
.location
,
790 rotation
=(0.0, 0.0, 0.0))
791 if atom_shape
== '2': #Cube
792 bpy
.ops
.mesh
.primitive_cube_add(
794 enter_editmode
=False,
795 location
=atom
.location
,
796 rotation
=(0.0, 0.0, 0.0))
797 if atom_shape
== '3': #Plane
798 bpy
.ops
.mesh
.primitive_plane_add(
800 enter_editmode
=False,
801 location
=atom
.location
,
802 rotation
=(0.0, 0.0, 0.0))
803 if atom_shape
== '4a': #Circle
804 bpy
.ops
.mesh
.primitive_circle_add(
809 enter_editmode
=False,
810 location
=atom
.location
,
812 if atom_shape
== '4b': #Circle NURBS
813 bpy
.ops
.surface
.primitive_nurbs_surface_circle_add(
815 enter_editmode
=False,
816 location
=atom
.location
,
818 if atom_shape
in {'5a','5b','5c','5d','5e'}: #Icosphere
819 index
= {'5a':1,'5b':2,'5c':3,'5d':4,'5e':5}
820 bpy
.ops
.mesh
.primitive_ico_sphere_add(
821 subdivisions
=int(index
[atom_shape
]),
824 enter_editmode
=False,
825 location
=atom
.location
,
827 if atom_shape
== '6a': #Cylinder
828 bpy
.ops
.mesh
.primitive_cylinder_add(
832 end_fill_type
='NGON',
834 enter_editmode
=False,
835 location
=atom
.location
,
837 if atom_shape
== '6b': #Cylinder NURBS
838 bpy
.ops
.surface
.primitive_nurbs_surface_cylinder_add(
840 enter_editmode
=False,
841 location
=atom
.location
,
843 if atom_shape
== '7': #Cone
844 bpy
.ops
.mesh
.primitive_cone_add(
849 end_fill_type
='NGON',
851 enter_editmode
=False,
852 location
=atom
.location
,
854 if atom_shape
== '8a': #Torus
855 bpy
.ops
.mesh
.primitive_torus_add(
857 location
=atom
.location
,
865 if atom_shape
== '8b': #Torus NURBS
866 bpy
.ops
.surface
.primitive_nurbs_surface_torus_add(
868 enter_editmode
=False,
869 location
=atom
.location
,
872 new_atom
= bpy
.context
.view_layer
.objects
.active
873 new_atom
.scale
= atom
.scale
+ Vector((0.0,0.0,0.0))
874 new_atom
.name
= atom
.name
875 new_atom
.select_set(True)
877 new_atom
.active_material
= new_material
879 # If it is the representative object of a duplivert structure then
880 # transfer the parent and hide the new object.
881 if atom
.parent
!= None:
882 new_atom
.parent
= atom
.parent
883 new_atom
.hide_set(True)
885 # Note the collection where the old object was placed into.
886 coll_old_atom
= get_collection_object(atom
)
888 # Note the collection where the new object was placed into.
889 coll_new_atom_past
= get_collection_object(new_atom
)
891 # If it is not the same collection then ...
892 if coll_new_atom_past
!= coll_old_atom
:
893 # Put the new object into the collection of the old object and ...
894 coll_old_atom
.objects
.link(new_atom
)
895 # ... unlink the new atom from its original collection.
896 coll_new_atom_past
.objects
.unlink(new_atom
)
898 # If necessary, remove the childrens of the old object.
899 for child
in atom
.children
:
900 bpy
.ops
.object.select_all(action
='DESELECT')
902 child
.select_set(True)
904 coll_child
= get_collection_object(child
)
905 coll_child
.objects
.unlink(child
)
906 bpy
.ops
.object.delete()
908 # Deselect everything
909 bpy
.ops
.object.select_all(action
='DESELECT')
910 # Make the old atom visible.
912 # Select the old atom.
913 atom
.select_set(True)
914 # Remove the parent if necessary.
916 # Unlink the old object from the collection.
917 coll_old_atom
.objects
.unlink(atom
)
918 # Delete the old atom
919 bpy
.ops
.object.delete()
921 #if "_F2+_center" or "_F+_center" or "_F0_center" in coll_old_atom:
922 # print("Delete the collection")
927 # Draw a special object (e.g. halo, etc. ...)
928 def draw_obj_special(atom_shape
, atom
):
930 # Note the collection where 'atom' is placed into.
931 coll_atom
= get_collection_object(atom
)
933 # Now, create a collection for the new objects
935 # Create the new collection and ...
936 coll_new
= bpy
.data
.collections
.new(coll_new
)
937 # ... link it to the collection, which contains 'atom'.
938 coll_atom
.children
.link(coll_new
)
940 # Get the color of the selected atom.
941 material
= atom
.active_material
942 mat_P_BSDF_default
= next(n
for n
in material
.node_tree
.nodes
943 if n
.type == "BSDF_PRINCIPLED")
944 default_color
= mat_P_BSDF_default
.inputs
['Base Color'].default_value
946 # Create first a cube
947 bpy
.ops
.mesh
.primitive_cube_add(align
='WORLD',
948 enter_editmode
=False,
949 location
=atom
.location
,
950 rotation
=(0.0, 0.0, 0.0))
951 cube
= bpy
.context
.view_layer
.objects
.active
952 cube
.scale
= atom
.scale
+ Vector((0.0,0.0,0.0))
953 cube
.select_set(True)
956 if atom_shape
== '1':
957 cube
.name
= atom
.name
+ "_F2+_vac"
959 # New material for this cube
960 material_new
= bpy
.data
.materials
.new(atom
.name
+ "_F2+_vac")
961 material_new
.use_nodes
= True
962 mat_P_BSDF
= next(n
for n
in material_new
.node_tree
.nodes
963 if n
.type == "BSDF_PRINCIPLED")
964 mat_P_BSDF
.inputs
['Base Color'].default_value
= default_color
965 mat_P_BSDF
.inputs
['Metallic'].default_value
= 0.7
966 mat_P_BSDF
.inputs
['Specular'].default_value
= 0.0
967 mat_P_BSDF
.inputs
['Roughness'].default_value
= 0.65
968 mat_P_BSDF
.inputs
['Clearcoat Roughness'].default_value
= 0.0
969 mat_P_BSDF
.inputs
['IOR'].default_value
= 1.45
970 mat_P_BSDF
.inputs
['Transmission'].default_value
= 0.6
971 mat_P_BSDF
.inputs
['Transmission Roughness'].default_value
= 0.5
972 mat_P_BSDF
.inputs
['Alpha'].default_value
= 0.6
973 # Some additional stuff for eevee.
974 material_new
.blend_method
= 'HASHED'
975 material_new
.shadow_method
= 'HASHED'
976 material_new
.use_backface_culling
= False
977 cube
.active_material
= material_new
979 # Put a point lamp inside the defect.
980 lamp_data
= bpy
.data
.lights
.new(name
=atom
.name
+ "_F2+_lamp", type="POINT")
981 lamp_data
.distance
= atom
.scale
[0] * 2.0
982 lamp_data
.energy
= 2000.0
983 lamp_data
.color
= (0.8, 0.8, 0.8)
984 lamp
= bpy
.data
.objects
.new(atom
.name
+ "_F2+_lamp", lamp_data
)
985 lamp
.location
= Vector((0.0, 0.0, 0.0))
986 bpy
.context
.collection
.objects
.link(lamp
)
989 # The new 'atom' is the F2+ defect
992 # Note the collection where all the new objects were placed into.
993 # We use only one object, the cube
994 coll_ori
= get_collection_object(cube
)
996 # If it is not the same collection then ...
997 if coll_ori
!= coll_new
:
998 # Put all new objects into the new collection and ...
999 coll_new
.objects
.link(cube
)
1000 coll_new
.objects
.link(lamp
)
1001 # ... unlink them from their original collection.
1002 coll_ori
.objects
.unlink(cube
)
1003 coll_ori
.objects
.unlink(lamp
)
1005 coll_new
.name
= atom
.name
+ "_F2+_center"
1007 if atom
.parent
!= None:
1008 cube
.parent
= atom
.parent
1013 if atom_shape
== '2':
1014 cube
.name
= atom
.name
+ "_F2+_vac"
1016 # New material for this cube
1017 material_new
= bpy
.data
.materials
.new(atom
.name
+ "_F2+_vac")
1018 material_new
.use_nodes
= True
1019 mat_P_BSDF
= next(n
for n
in material_new
.node_tree
.nodes
1020 if n
.type == "BSDF_PRINCIPLED")
1021 mat_P_BSDF
.inputs
['Base Color'].default_value
= [0.0, 0.0, 0.8, 1.0]
1022 mat_P_BSDF
.inputs
['Metallic'].default_value
= 0.7
1023 mat_P_BSDF
.inputs
['Specular'].default_value
= 0.0
1024 mat_P_BSDF
.inputs
['Roughness'].default_value
= 0.65
1025 mat_P_BSDF
.inputs
['Clearcoat Roughness'].default_value
= 0.0
1026 mat_P_BSDF
.inputs
['IOR'].default_value
= 1.45
1027 mat_P_BSDF
.inputs
['Transmission'].default_value
= 0.6
1028 mat_P_BSDF
.inputs
['Transmission Roughness'].default_value
= 0.5
1029 mat_P_BSDF
.inputs
['Alpha'].default_value
= 0.6
1030 # Some additional stuff for eevee.
1031 material_new
.blend_method
= 'HASHED'
1032 material_new
.shadow_method
= 'HASHED'
1033 material_new
.use_backface_culling
= False
1034 cube
.active_material
= material_new
1036 # Create now an electron
1037 scale
= atom
.scale
/ 10.0
1038 bpy
.ops
.surface
.primitive_nurbs_surface_sphere_add(
1040 enter_editmode
=False,
1041 location
=(0.0, 0.0, 0.0),
1042 rotation
=(0.0, 0.0, 0.0))
1043 electron
= bpy
.context
.view_layer
.objects
.active
1044 electron
.scale
= scale
1045 electron
.name
= atom
.name
+ "_F+_electron"
1046 electron
.parent
= cube
1047 # New material for the electron
1048 material_electron
= bpy
.data
.materials
.new(atom
.name
+ "_F+-center")
1049 material_electron
.use_nodes
= True
1050 mat_P_BSDF
= next(n
for n
in material_electron
.node_tree
.nodes
1051 if n
.type == "BSDF_PRINCIPLED")
1052 mat_P_BSDF
.inputs
['Base Color'].default_value
= [0.0, 0.0, 0.8, 1.0]
1053 mat_P_BSDF
.inputs
['Metallic'].default_value
= 0.8
1054 mat_P_BSDF
.inputs
['Specular'].default_value
= 0.0
1055 mat_P_BSDF
.inputs
['Roughness'].default_value
= 0.3
1056 mat_P_BSDF
.inputs
['Clearcoat Roughness'].default_value
= 0.0
1057 mat_P_BSDF
.inputs
['IOR'].default_value
= 1.45
1058 mat_P_BSDF
.inputs
['Transmission'].default_value
= 0.6
1059 mat_P_BSDF
.inputs
['Transmission Roughness'].default_value
= 0.5
1060 mat_P_BSDF
.inputs
['Alpha'].default_value
= 1.0
1061 # Some additional stuff for eevee.
1062 material_electron
.blend_method
= 'OPAQUE'
1063 material_electron
.shadow_method
= 'OPAQUE'
1064 material_electron
.use_backface_culling
= False
1065 electron
.active_material
= material_electron
1067 # Put a point lamp inside the electron
1068 lamp_data
= bpy
.data
.lights
.new(name
=atom
.name
+ "_F+_lamp", type="POINT")
1069 lamp_data
.distance
= atom
.scale
[0] * 2.0
1070 lamp_data
.energy
= 100000.0
1071 lamp_data
.color
= (0.0, 0.0, 0.8)
1072 lamp
= bpy
.data
.objects
.new(atom
.name
+ "_F+_lamp", lamp_data
)
1073 lamp
.location
= Vector((scale
[0]*1.5, 0.0, 0.0))
1074 bpy
.context
.collection
.objects
.link(lamp
)
1077 # The new 'atom' is the F+ defect complex + lamp
1080 # Note the collection where all the new objects were placed into.
1081 # We use only one object, the cube
1082 coll_ori
= get_collection_object(cube
)
1084 # If it is not the same collection then ...
1085 if coll_ori
!= coll_new
:
1086 # Put all new objects into the new collection and ...
1087 coll_new
.objects
.link(cube
)
1088 coll_new
.objects
.link(electron
)
1089 coll_new
.objects
.link(lamp
)
1090 # ... unlink them from their original collection.
1091 coll_ori
.objects
.unlink(cube
)
1092 coll_ori
.objects
.unlink(electron
)
1093 coll_ori
.objects
.unlink(lamp
)
1095 coll_new
.name
= atom
.name
+ "_F+_center"
1097 if atom
.parent
!= None:
1098 cube
.parent
= atom
.parent
1100 electron
.hide_set(True)
1104 if atom_shape
== '3':
1105 cube
.name
= atom
.name
+ "_F2+_vac"
1107 # New material for this cube
1108 material_new
= bpy
.data
.materials
.new(atom
.name
+ "_F2+_vac")
1109 material_new
.use_nodes
= True
1110 mat_P_BSDF
= next(n
for n
in material_new
.node_tree
.nodes
1111 if n
.type == "BSDF_PRINCIPLED")
1112 mat_P_BSDF
.inputs
['Base Color'].default_value
= [0.8, 0.0, 0.0, 1.0]
1113 mat_P_BSDF
.inputs
['Metallic'].default_value
= 0.7
1114 mat_P_BSDF
.inputs
['Specular'].default_value
= 0.0
1115 mat_P_BSDF
.inputs
['Roughness'].default_value
= 0.65
1116 mat_P_BSDF
.inputs
['Clearcoat Roughness'].default_value
= 0.0
1117 mat_P_BSDF
.inputs
['IOR'].default_value
= 1.45
1118 mat_P_BSDF
.inputs
['Transmission'].default_value
= 0.6
1119 mat_P_BSDF
.inputs
['Transmission Roughness'].default_value
= 0.5
1120 mat_P_BSDF
.inputs
['Alpha'].default_value
= 0.6
1121 # Some additional stuff for eevee.
1122 material_new
.blend_method
= 'HASHED'
1123 material_new
.shadow_method
= 'HASHED'
1124 material_new
.use_backface_culling
= False
1125 cube
.active_material
= material_new
1127 # Create now two electrons ... .
1128 scale
= atom
.scale
/ 10.0
1129 bpy
.ops
.surface
.primitive_nurbs_surface_sphere_add(
1131 enter_editmode
=False,
1132 location
=(scale
[0]*1.5,0.0,0.0),
1133 rotation
=(0.0, 0.0, 0.0))
1134 electron1
= bpy
.context
.view_layer
.objects
.active
1135 electron1
.scale
= scale
1136 electron1
.name
= atom
.name
+ "_F0_electron_1"
1137 electron1
.parent
= cube
1138 bpy
.ops
.surface
.primitive_nurbs_surface_sphere_add(
1140 enter_editmode
=False,
1141 location
=(-scale
[0]*1.5,0.0,0.0),
1142 rotation
=(0.0, 0.0, 0.0))
1143 electron2
= bpy
.context
.view_layer
.objects
.active
1144 electron2
.scale
= scale
1145 electron2
.name
= atom
.name
+ "_F0_electron_2"
1146 electron2
.parent
= cube
1147 # Create a new material for the two electrons.
1148 material_electron
= bpy
.data
.materials
.new(atom
.name
+ "_F0-center")
1149 material_electron
.use_nodes
= True
1150 mat_P_BSDF
= next(n
for n
in material_electron
.node_tree
.nodes
1151 if n
.type == "BSDF_PRINCIPLED")
1152 mat_P_BSDF
.inputs
['Base Color'].default_value
= [0.0, 0.0, 0.8, 1.0]
1153 mat_P_BSDF
.inputs
['Metallic'].default_value
= 0.8
1154 mat_P_BSDF
.inputs
['Specular'].default_value
= 0.0
1155 mat_P_BSDF
.inputs
['Roughness'].default_value
= 0.3
1156 mat_P_BSDF
.inputs
['Clearcoat Roughness'].default_value
= 0.0
1157 mat_P_BSDF
.inputs
['IOR'].default_value
= 1.45
1158 mat_P_BSDF
.inputs
['Transmission'].default_value
= 0.6
1159 mat_P_BSDF
.inputs
['Transmission Roughness'].default_value
= 0.5
1160 mat_P_BSDF
.inputs
['Alpha'].default_value
= 1.0
1161 # Some additional stuff for eevee.
1162 material_electron
.blend_method
= 'OPAQUE'
1163 material_electron
.shadow_method
= 'OPAQUE'
1164 material_electron
.use_backface_culling
= False
1165 # We assign the materials to the two electrons.
1166 electron1
.active_material
= material_electron
1167 electron2
.active_material
= material_electron
1169 # Put two point lamps inside the electrons.
1170 lamp1_data
= bpy
.data
.lights
.new(name
=atom
.name
+ "_F0_lamp_1", type="POINT")
1171 lamp1_data
.distance
= atom
.scale
[0] * 2.0
1172 lamp1_data
.energy
= 20000.0
1173 lamp1_data
.color
= (0.8, 0.0, 0.0)
1174 lamp1
= bpy
.data
.objects
.new(atom
.name
+ "_F0_lamp", lamp1_data
)
1175 lamp1
.location
= Vector((scale
[0]*1.5, 0.0, 0.0))
1176 bpy
.context
.collection
.objects
.link(lamp1
)
1178 lamp2_data
= bpy
.data
.lights
.new(name
=atom
.name
+ "_F0_lamp_2", type="POINT")
1179 lamp2_data
.distance
= atom
.scale
[0] * 2.0
1180 lamp2_data
.energy
= 20000.0
1181 lamp2_data
.color
= (0.8, 0.0, 0.0)
1182 lamp2
= bpy
.data
.objects
.new(atom
.name
+ "_F0_lamp", lamp2_data
)
1183 lamp2
.location
= Vector((-scale
[0]*1.5, 0.0, 0.0))
1184 bpy
.context
.collection
.objects
.link(lamp2
)
1187 # The new 'atom' is the F0 defect complex + lamps
1190 # Note the collection where all the new objects were placed into.
1191 # We use only one object, the cube
1192 coll_ori
= get_collection_object(cube
)
1194 # If it is not the same collection then ...
1195 if coll_ori
!= coll_new
:
1196 # Put all new objects into the collection of 'atom' and ...
1197 coll_new
.objects
.link(cube
)
1198 coll_new
.objects
.link(electron1
)
1199 coll_new
.objects
.link(electron2
)
1200 coll_new
.objects
.link(lamp1
)
1201 coll_new
.objects
.link(lamp2
)
1202 # ... unlink them from their original collection.
1203 coll_ori
.objects
.unlink(cube
)
1204 coll_ori
.objects
.unlink(electron1
)
1205 coll_ori
.objects
.unlink(electron2
)
1206 coll_ori
.objects
.unlink(lamp1
)
1207 coll_ori
.objects
.unlink(lamp2
)
1209 coll_new
.name
= atom
.name
+ "_F0_center"
1211 if atom
.parent
!= None:
1212 cube
.parent
= atom
.parent
1214 electron1
.hide_set(True)
1215 electron2
.hide_set(True)
1216 lamp1
.hide_set(True)
1217 lamp2
.hide_set(True)
1219 # Deselect everything
1220 bpy
.ops
.object.select_all(action
='DESELECT')
1221 # Make the old atom visible.
1223 # Select the old atom.
1224 atom
.select_set(True)
1225 # Remove the parent if necessary.
1227 # Unlink the old object from the collection.
1228 coll_atom
.objects
.unlink(atom
)
1229 # Delete the old atom
1230 bpy
.ops
.object.delete()
1235 # Initialization of the list 'ELEMENTS'.
1236 def read_elements():
1240 for item
in ELEMENTS_DEFAULT
:
1242 # All three radii into a list
1243 radii
= [item
[4],item
[5],item
[6]]
1244 # The handling of the ionic radii will be done later. So far, it is an
1246 radii_ionic
= item
[7:]
1248 li
= ElementProp(item
[0], item
[1], item
[2], item
[3], radii
, radii_ionic
, [], [])
1253 # Custom data file: changing color and radii by using the list 'ELEMENTS'.
1254 def custom_datafile_change_atom_props():
1256 for atom
in bpy
.context
.selected_objects
:
1259 if len(atom
.children
) != 0:
1260 child
= atom
.children
[0]
1261 if child
.type in {'SURFACE', 'MESH', 'META'}:
1262 for element
in ELEMENTS
:
1263 if element
.name
in atom
.name
:
1268 if atom
.type in {'SURFACE', 'MESH', 'META'}:
1269 for element
in ELEMENTS
:
1270 if element
.name
in atom
.name
:
1276 obj
.scale
= (e
.radii
[0],) * 3
1277 mat
= obj
.active_material
1278 mat_P_BSDF
= next(n
for n
in mat
.node_tree
.nodes
1279 if n
.type == "BSDF_PRINCIPLED")
1281 mat_P_BSDF
.inputs
['Base Color'].default_value
= e
.color
1282 mat_P_BSDF
.subsurface_method
= e
.mat_P_BSDF
.Subsurface_method
1283 mat_P_BSDF
.distribution
= e
.mat_P_BSDF
.Distribution
1284 mat_P_BSDF
.inputs
['Subsurface'].default_value
= e
.mat_P_BSDF
.Subsurface
1285 mat_P_BSDF
.inputs
['Subsurface Color'].default_value
= e
.mat_P_BSDF
.Subsurface_color
1286 mat_P_BSDF
.inputs
['Subsurface Radius'].default_value
= e
.mat_P_BSDF
.Subsurface_radius
1287 mat_P_BSDF
.inputs
['Metallic'].default_value
= e
.mat_P_BSDF
.Metallic
1288 mat_P_BSDF
.inputs
['Specular'].default_value
= e
.mat_P_BSDF
.Specular
1289 mat_P_BSDF
.inputs
['Specular Tint'].default_value
= e
.mat_P_BSDF
.Specular_tilt
1290 mat_P_BSDF
.inputs
['Roughness'].default_value
= e
.mat_P_BSDF
.Roughness
1291 mat_P_BSDF
.inputs
['Anisotropic'].default_value
= e
.mat_P_BSDF
.Anisotropic
1292 mat_P_BSDF
.inputs
['Anisotropic Rotation'].default_value
= e
.mat_P_BSDF
.Anisotropic_rotation
1293 mat_P_BSDF
.inputs
['Sheen'].default_value
= e
.mat_P_BSDF
.Sheen
1294 mat_P_BSDF
.inputs
['Sheen Tint'].default_value
= e
.mat_P_BSDF
.Sheen_tint
1295 mat_P_BSDF
.inputs
['Clearcoat'].default_value
= e
.mat_P_BSDF
.Clearcoat
1296 mat_P_BSDF
.inputs
['Clearcoat Roughness'].default_value
= e
.mat_P_BSDF
.Clearcoat_rough
1297 mat_P_BSDF
.inputs
['IOR'].default_value
= e
.mat_P_BSDF
.IOR
1298 mat_P_BSDF
.inputs
['Transmission'].default_value
= e
.mat_P_BSDF
.Trans
1299 mat_P_BSDF
.inputs
['Transmission Roughness'].default_value
= e
.mat_P_BSDF
.Trans_rough
1300 mat_P_BSDF
.inputs
['Emission'].default_value
= e
.mat_P_BSDF
.Emission
1301 mat_P_BSDF
.inputs
['Emission Strength'].default_value
= e
.mat_P_BSDF
.Emission_strength
1302 mat_P_BSDF
.inputs
['Alpha'].default_value
= e
.mat_P_BSDF
.Alpha
1304 mat
.use_backface_culling
= e
.mat_Eevee
.use_backface
1305 mat
.blend_method
= e
.mat_Eevee
.blend_method
1306 mat
.shadow_method
= e
.mat_Eevee
.shadow_method
1307 mat
.alpha_threshold
= e
.mat_Eevee
.clip_threshold
1308 mat
.use_screen_refraction
= e
.mat_Eevee
.use_screen_refraction
1309 mat
.refraction_depth
= e
.mat_Eevee
.refraction_depth
1310 mat
.use_sss_translucency
= e
.mat_Eevee
.use_sss_translucency
1311 mat
.pass_index
= e
.mat_Eevee
.pass_index
1315 bpy
.ops
.object.select_all(action
='DESELECT')
1318 # Reading a custom data file and modifying the list 'ELEMENTS'.
1319 def custom_datafile(path_datafile
):
1321 if path_datafile
== "":
1324 path_datafile
= bpy
.path
.abspath(path_datafile
)
1326 if os
.path
.isfile(path_datafile
) == False:
1329 # The whole list gets deleted! We build it new.
1332 # Read the data file, which contains all data
1333 # (atom name, radii, colors, etc.)
1334 data_file_p
= open(path_datafile
, "r")
1336 for line
in data_file_p
:
1343 list_radii_ionic
= []
1346 if len(line
) in [0,1]:
1350 if "Number :" in line
:
1351 pos
= line
.rfind(':') + 1
1352 number
= line
[pos
:].strip()
1354 if "Name :" in line
:
1355 pos
= line
.rfind(':') + 1
1356 name
= line
[pos
:].strip()
1358 if "Short name :" in line
:
1359 pos
= line
.rfind(':') + 1
1360 short_name
= line
[pos
:].strip()
1362 if "Color :" in line
:
1363 pos
= line
.rfind(':') + 1
1364 color_value
= line
[pos
:].strip().split(',')
1365 color
= [float(color_value
[0]),
1366 float(color_value
[1]),
1367 float(color_value
[2]),
1368 float(color_value
[3])]
1370 if "Radius used :" in line
:
1371 pos
= line
.rfind(':') + 1
1372 radius_used
= float(line
[pos
:].strip())
1374 if "Radius, covalent :" in line
:
1375 pos
= line
.rfind(':') + 1
1376 radius_covalent
= float(line
[pos
:].strip())
1378 if "Radius, atomic :" in line
:
1379 pos
= line
.rfind(':') + 1
1380 radius_atomic
= float(line
[pos
:].strip())
1381 if "Charge state :" in line
:
1382 pos
= line
.rfind(':') + 1
1383 charge_state
= float(line
[pos
:].strip())
1384 line
= data_file_p
.readline()
1385 pos
= line
.rfind(':') + 1
1386 radius_ionic
= float(line
[pos
:].strip())
1387 list_radii_ionic
.append(charge_state
)
1388 list_radii_ionic
.append(radius_ionic
)
1390 # Some Principled BSDF properties
1393 if "P BSDF Subsurface method :" in line
:
1394 pos
= line
.rfind(':') + 1
1395 P_BSDF_subsurface_method
= line
[pos
:].strip()
1396 if "P BSDF Distribution :" in line
:
1397 pos
= line
.rfind(':') + 1
1398 P_BSDF_distribution
= line
[pos
:].strip()
1399 if "P BSDF Subsurface :" in line
:
1400 pos
= line
.rfind(':') + 1
1401 P_BSDF_subsurface
= float(line
[pos
:].strip())
1402 if "P BSDF Subsurface Color :" in line
:
1403 pos
= line
.rfind(':') + 1
1404 color_value
= line
[pos
:].strip().split(',')
1405 P_BSDF_subsurface_color
= [float(color_value
[0]),
1406 float(color_value
[1]),
1407 float(color_value
[2]),
1408 float(color_value
[3])]
1409 if "P BSDF Subsurface Radius :" in line
:
1410 pos
= line
.rfind(':') + 1
1411 radii_values
= line
[pos
:].strip().split(',')
1412 P_BSDF_subsurface_radius
= [float(color_value
[0]),
1413 float(color_value
[1]),
1414 float(color_value
[2])]
1415 if "P BSDF Metallic :" in line
:
1416 pos
= line
.rfind(':') + 1
1417 P_BSDF_metallic
= float(line
[pos
:].strip())
1418 if "P BSDF Specular :" in line
:
1419 pos
= line
.rfind(':') + 1
1420 P_BSDF_specular
= float(line
[pos
:].strip())
1421 if "P BSDF Specular Tilt :" in line
:
1422 pos
= line
.rfind(':') + 1
1423 P_BSDF_specular_tilt
= float(line
[pos
:].strip())
1424 if "P BSDF Roughness :" in line
:
1425 pos
= line
.rfind(':') + 1
1426 P_BSDF_roughness
= float(line
[pos
:].strip())
1427 if "P BSDF Anisotropic :" in line
:
1428 pos
= line
.rfind(':') + 1
1429 P_BSDF_anisotropic
= float(line
[pos
:].strip())
1430 if "P BSDF Anisotropic Rotation :" in line
:
1431 pos
= line
.rfind(':') + 1
1432 P_BSDF_anisotropic_rotation
= float(line
[pos
:].strip())
1433 if "P BSDF Sheen : " in line
:
1434 pos
= line
.rfind(':') + 1
1435 P_BSDF_sheen
= float(line
[pos
:].strip())
1436 if "P BSDF Sheen Tint : " in line
:
1437 pos
= line
.rfind(':') + 1
1438 P_BSDF_sheen_tint
= float(line
[pos
:].strip())
1439 if "P BSDF Clearcoat :" in line
:
1440 pos
= line
.rfind(':') + 1
1441 P_BSDF_clearcoat
= float(line
[pos
:].strip())
1442 if "P BSDF Clearcoat Rough :" in line
:
1443 pos
= line
.rfind(':') + 1
1444 P_BSDF_clearcoat_roughness
= float(line
[pos
:].strip())
1445 if "P BSDF IOR :" in line
:
1446 pos
= line
.rfind(':') + 1
1447 P_BSDF_IOR
= float(line
[pos
:].strip())
1448 if "P BSDF Trans :" in line
:
1449 pos
= line
.rfind(':') + 1
1450 P_BSDF_transparency
= float(line
[pos
:].strip())
1451 if "P BSDF Trans Roughness :" in line
:
1452 pos
= line
.rfind(':') + 1
1453 P_BSDF_transparency_roughness
= float(line
[pos
:].strip())
1454 if "P BSDF Emisssion : " in line
:
1455 pos
= line
.rfind(':') + 1
1456 color_value
= line
[pos
:].strip().split(',')
1457 P_BSDF_emission
= [float(color_value
[0]),
1458 float(color_value
[1]),
1459 float(color_value
[2]),
1460 float(color_value
[3])]
1461 if "P BSDF Emission Strength :" in line
:
1462 pos
= line
.rfind(':') + 1
1463 P_BSDF_emission_strength
= float(line
[pos
:].strip())
1464 if "P BSDF Alpha :" in line
:
1465 pos
= line
.rfind(':') + 1
1466 P_BSDF_alpha
= float(line
[pos
:].strip())
1468 if "Eevee Use Backface Culling :" in line
:
1469 pos
= line
.rfind(':') + 1
1470 line
= line
[pos
:].strip()
1471 if line
.lower() in ("yes", "true", "1"):
1472 Eevee_use_backface
= True
1474 Eevee_use_backface
= False
1475 if "Eevee Blend Method :" in line
:
1476 pos
= line
.rfind(':') + 1
1477 Eevee_blend_method
= line
[pos
:].strip()
1478 if "Eevee Shadow Method :" in line
:
1479 pos
= line
.rfind(':') + 1
1480 Eevee_shadow_method
= line
[pos
:].strip()
1481 if "Eevee Clip Threshold :" in line
:
1482 pos
= line
.rfind(':') + 1
1483 Eevee_clip_threshold
= float(line
[pos
:].strip())
1484 if "Eevee Use Screen Refraction :" in line
:
1485 pos
= line
.rfind(':') + 1
1486 line
= line
[pos
:].strip()
1487 if line
.lower() in ("yes", "true", "1"):
1488 Eevee_use_screen_refraction
= True
1490 Eevee_use_screen_refraction
= False
1491 if "Eevee Refraction depth : " in line
:
1492 pos
= line
.rfind(':') + 1
1493 Eevee_refraction_depth
= float(line
[pos
:].strip())
1494 if "Eevee Use SSS Translucency :" in line
:
1495 pos
= line
.rfind(':') + 1
1496 line
= line
[pos
:].strip()
1497 if line
.lower() in ("yes", "true", "1"):
1498 Eevee_use_sss_translucency
= True
1500 Eevee_use_sss_translucency
= False
1501 if "Eevee Pass Index :" in line
:
1502 pos
= line
.rfind(':') + 1
1503 Eevee_pass_index
= int(line
[pos
:].strip())
1506 line
= data_file_p
.readline()
1508 list_radii
= [radius_used
, radius_covalent
, radius_atomic
]
1510 Eevee_material
= EeveeProp(Eevee_use_backface
,
1512 Eevee_shadow_method
,
1513 Eevee_clip_threshold
,
1514 Eevee_use_screen_refraction
,
1515 Eevee_refraction_depth
,
1516 Eevee_use_sss_translucency
,
1519 P_BSDF_material
= PBSDFProp(P_BSDF_subsurface_method
,
1520 P_BSDF_distribution
,
1522 P_BSDF_subsurface_color
,
1523 P_BSDF_subsurface_radius
,
1526 P_BSDF_specular_tilt
,
1529 P_BSDF_anisotropic_rotation
,
1533 P_BSDF_clearcoat_roughness
,
1535 P_BSDF_transparency
,
1536 P_BSDF_transparency_roughness
,
1538 P_BSDF_emission_strength
,
1541 element
= ElementProp(number
,
1550 ELEMENTS
.append(element
)