1 # SPDX-FileCopyrightText: 2019-2023 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
8 from mathutils
import Vector
12 # -----------------------------------------------------------------------------
13 # Atom and element data
16 # This is a list that contains some data of all possible elements. The structure
19 # 1, "Hydrogen", "H", [0.0,0.0,1.0], 0.32, 0.32, 0.32 , -1 , 1.54 means
21 # No., name, short name, color, radius (used), radius (covalent), radius (atomic),
23 # charge state 1, radius (ionic) 1, charge state 2, radius (ionic) 2, ... all
24 # charge states for any atom are listed, if existing.
25 # The list is fixed and cannot be changed ... (see below)
28 ( 1, "Hydrogen", "H", ( 1.0, 1.0, 1.0, 1.0), 0.32, 0.32, 0.79 , -1 , 1.54 ),
29 ( 2, "Helium", "He", ( 0.85, 1.0, 1.0, 1.0), 0.93, 0.93, 0.49 ),
30 ( 3, "Lithium", "Li", ( 0.8, 0.50, 1.0, 1.0), 1.23, 1.23, 2.05 , 1 , 0.68 ),
31 ( 4, "Beryllium", "Be", ( 0.76, 1.0, 0.0, 1.0), 0.90, 0.90, 1.40 , 1 , 0.44 , 2 , 0.35 ),
32 ( 5, "Boron", "B", ( 1.0, 0.70, 0.70, 1.0), 0.82, 0.82, 1.17 , 1 , 0.35 , 3 , 0.23 ),
33 ( 6, "Carbon", "C", ( 0.56, 0.56, 0.56, 1.0), 0.77, 0.77, 0.91 , -4 , 2.60 , 4 , 0.16 ),
34 ( 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 ),
35 ( 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 ),
36 ( 9, "Fluorine", "F", ( 0.56, 0.87, 0.31, 1.0), 0.72, 0.72, 0.57 , -1 , 1.33 , 7 , 0.08 ),
37 (10, "Neon", "Ne", ( 0.70, 0.89, 0.96, 1.0), 0.71, 0.71, 0.51 , 1 , 1.12 ),
38 (11, "Sodium", "Na", ( 0.67, 0.36, 0.94, 1.0), 1.54, 1.54, 2.23 , 1 , 0.97 ),
39 (12, "Magnesium", "Mg", ( 0.54, 1.0, 0.0, 1.0), 1.36, 1.36, 1.72 , 1 , 0.82 , 2 , 0.66 ),
40 (13, "Aluminium", "Al", ( 0.74, 0.65, 0.65, 1.0), 1.18, 1.18, 1.82 , 3 , 0.51 ),
41 (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 ),
42 (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 ),
43 (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 ),
44 (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 ),
45 (18, "Argon", "Ar", ( 0.50, 0.81, 0.89, 1.0), 0.98, 0.98, 0.88 , 1 , 1.54 ),
46 (19, "Potassium", "K", ( 0.56, 0.25, 0.83, 1.0), 2.03, 2.03, 2.77 , 1 , 0.81 ),
47 (20, "Calcium", "Ca", ( 0.23, 1.0, 0.0, 1.0), 1.74, 1.74, 2.23 , 1 , 1.18 , 2 , 0.99 ),
48 (21, "Scandium", "Sc", ( 0.90, 0.90, 0.90, 1.0), 1.44, 1.44, 2.09 , 3 , 0.73 ),
49 (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 ),
50 (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 ),
51 (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 ),
52 (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 ),
53 (26, "Iron", "Fe", ( 0.87, 0.4, 0.2, 1.0), 1.17, 1.17, 1.72 , 2 , 0.74 , 3 , 0.64 ),
54 (27, "Cobalt", "Co", ( 0.94, 0.56, 0.62, 1.0), 1.16, 1.16, 1.67 , 2 , 0.72 , 3 , 0.63 ),
55 (28, "Nickel", "Ni", ( 0.31, 0.81, 0.31, 1.0), 1.15, 1.15, 1.62 , 2 , 0.69 ),
56 (29, "Copper", "Cu", ( 0.78, 0.50, 0.2, 1.0), 1.17, 1.17, 1.57 , 1 , 0.96 , 2 , 0.72 ),
57 (30, "Zinc", "Zn", ( 0.49, 0.50, 0.69, 1.0), 1.25, 1.25, 1.53 , 1 , 0.88 , 2 , 0.74 ),
58 (31, "Gallium", "Ga", ( 0.76, 0.56, 0.56, 1.0), 1.26, 1.26, 1.81 , 1 , 0.81 , 3 , 0.62 ),
59 (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 ),
60 (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 ),
61 (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 ),
62 (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 ),
63 (36, "Krypton", "Kr", ( 0.36, 0.72, 0.81, 1.0), 1.31, 1.31, 1.24 ),
64 (37, "Rubidium", "Rb", ( 0.43, 0.18, 0.69, 1.0), 2.16, 2.16, 2.98 , 1 , 1.47 ),
65 (38, "Strontium", "Sr", ( 0.0, 1.0, 0.0, 1.0), 1.91, 1.91, 2.45 , 2 , 1.12 ),
66 (39, "Yttrium", "Y", ( 0.58, 1.0, 1.0, 1.0), 1.62, 1.62, 2.27 , 3 , 0.89 ),
67 (40, "Zirconium", "Zr", ( 0.58, 0.87, 0.87, 1.0), 1.45, 1.45, 2.16 , 1 , 1.09 , 4 , 0.79 ),
68 (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 ),
69 (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 ),
70 (43, "Technetium", "Tc", ( 0.23, 0.61, 0.61, 1.0), 1.27, 1.27, 1.95 , 7 , 0.97 ),
71 (44, "Ruthenium", "Ru", ( 0.14, 0.56, 0.56, 1.0), 1.25, 1.25, 1.89 , 4 , 0.67 ),
72 (45, "Rhodium", "Rh", ( 0.03, 0.49, 0.54, 1.0), 1.25, 1.25, 1.83 , 3 , 0.68 ),
73 (46, "Palladium", "Pd", ( 0.0, 0.41, 0.52, 1.0), 1.28, 1.28, 1.79 , 2 , 0.80 , 4 , 0.65 ),
74 (47, "Silver", "Ag", ( 0.75, 0.75, 0.75, 1.0), 1.34, 1.34, 1.75 , 1 , 1.26 , 2 , 0.89 ),
75 (48, "Cadmium", "Cd", ( 1.0, 0.85, 0.56, 1.0), 1.48, 1.48, 1.71 , 1 , 1.14 , 2 , 0.97 ),
76 (49, "Indium", "In", ( 0.65, 0.45, 0.45, 1.0), 1.44, 1.44, 2.00 , 3 , 0.81 ),
77 (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 ),
78 (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 ),
79 (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 ),
80 (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 ),
81 (54, "Xenon", "Xe", ( 0.25, 0.61, 0.69, 1.0), 1.31, 1.31, 1.24 ),
82 (55, "Caesium", "Cs", ( 0.34, 0.09, 0.56, 1.0), 2.35, 2.35, 3.35 , 1 , 1.67 ),
83 (56, "Barium", "Ba", ( 0.0, 0.78, 0.0, 1.0), 1.98, 1.98, 2.78 , 1 , 1.53 , 2 , 1.34 ),
84 (57, "Lanthanum", "La", ( 0.43, 0.83, 1.0, 1.0), 1.69, 1.69, 2.74 , 1 , 1.39 , 3 , 1.06 ),
85 (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 ),
86 (59, "Praseodymium", "Pr", ( 0.85, 1.0, 0.78, 1.0), 1.65, 1.65, 2.67 , 3 , 1.01 , 4 , 0.90 ),
87 (60, "Neodymium", "Nd", ( 0.78, 1.0, 0.78, 1.0), 1.64, 1.64, 2.64 , 3 , 0.99 ),
88 (61, "Promethium", "Pm", ( 0.63, 1.0, 0.78, 1.0), 1.63, 1.63, 2.62 , 3 , 0.97 ),
89 (62, "Samarium", "Sm", ( 0.56, 1.0, 0.78, 1.0), 1.62, 1.62, 2.59 , 3 , 0.96 ),
90 (63, "Europium", "Eu", ( 0.38, 1.0, 0.78, 1.0), 1.85, 1.85, 2.56 , 2 , 1.09 , 3 , 0.95 ),
91 (64, "Gadolinium", "Gd", ( 0.27, 1.0, 0.78, 1.0), 1.61, 1.61, 2.54 , 3 , 0.93 ),
92 (65, "Terbium", "Tb", ( 0.18, 1.0, 0.78, 1.0), 1.59, 1.59, 2.51 , 3 , 0.92 , 4 , 0.84 ),
93 (66, "Dysprosium", "Dy", ( 0.12, 1.0, 0.78, 1.0), 1.59, 1.59, 2.49 , 3 , 0.90 ),
94 (67, "Holmium", "Ho", ( 0.0, 1.0, 0.61, 1.0), 1.58, 1.58, 2.47 , 3 , 0.89 ),
95 (68, "Erbium", "Er", ( 0.0, 0.90, 0.45, 1.0), 1.57, 1.57, 2.45 , 3 , 0.88 ),
96 (69, "Thulium", "Tm", ( 0.0, 0.83, 0.32, 1.0), 1.56, 1.56, 2.42 , 3 , 0.87 ),
97 (70, "Ytterbium", "Yb", ( 0.0, 0.74, 0.21, 1.0), 1.74, 1.74, 2.40 , 2 , 0.93 , 3 , 0.85 ),
98 (71, "Lutetium", "Lu", ( 0.0, 0.67, 0.14, 1.0), 1.56, 1.56, 2.25 , 3 , 0.85 ),
99 (72, "Hafnium", "Hf", ( 0.30, 0.76, 1.0, 1.0), 1.44, 1.44, 2.16 , 4 , 0.78 ),
100 (73, "Tantalum", "Ta", ( 0.30, 0.65, 1.0, 1.0), 1.34, 1.34, 2.09 , 5 , 0.68 ),
101 (74, "Tungsten", "W", ( 0.12, 0.58, 0.83, 1.0), 1.30, 1.30, 2.02 , 4 , 0.70 , 6 , 0.62 ),
102 (75, "Rhenium", "Re", ( 0.14, 0.49, 0.67, 1.0), 1.28, 1.28, 1.97 , 4 , 0.72 , 7 , 0.56 ),
103 (76, "Osmium", "Os", ( 0.14, 0.4, 0.58, 1.0), 1.26, 1.26, 1.92 , 4 , 0.88 , 6 , 0.69 ),
104 (77, "Iridium", "Ir", ( 0.09, 0.32, 0.52, 1.0), 1.27, 1.27, 1.87 , 4 , 0.68 ),
105 (78, "Platinum", "Pt", ( 0.81, 0.81, 0.87, 1.0), 1.30, 1.30, 1.83 , 2 , 0.80 , 4 , 0.65 ),
106 (79, "Gold", "Au", ( 1.0, 0.81, 0.13, 1.0), 1.34, 1.34, 1.79 , 1 , 1.37 , 3 , 0.85 ),
107 (80, "Mercury", "Hg", ( 0.72, 0.72, 0.81, 1.0), 1.49, 1.49, 1.76 , 1 , 1.27 , 2 , 1.10 ),
108 (81, "Thallium", "Tl", ( 0.65, 0.32, 0.30, 1.0), 1.48, 1.48, 2.08 , 1 , 1.47 , 3 , 0.95 ),
109 (82, "Lead", "Pb", ( 0.34, 0.34, 0.38, 1.0), 1.47, 1.47, 1.81 , 2 , 1.20 , 4 , 0.84 ),
110 (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 ),
111 (84, "Polonium", "Po", ( 0.67, 0.36, 0.0, 1.0), 1.46, 1.46, 1.53 , 6 , 0.67 ),
112 (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 ),
113 (86, "Radon", "Rn", ( 0.25, 0.50, 0.58, 1.0), 1.00, 1.00, 1.34 ),
114 (87, "Francium", "Fr", ( 0.25, 0.0, 0.4, 1.0), 1.00, 1.00, 1.00 , 1 , 1.80 ),
115 (88, "Radium", "Ra", ( 0.0, 0.49, 0.0, 1.0), 1.00, 1.00, 1.00 , 2 , 1.43 ),
116 (89, "Actinium", "Ac", ( 0.43, 0.67, 0.98, 1.0), 1.00, 1.00, 1.00 , 3 , 1.18 ),
117 (90, "Thorium", "Th", ( 0.0, 0.72, 1.0, 1.0), 1.65, 1.65, 1.00 , 4 , 1.02 ),
118 (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 ),
119 (92, "Uranium", "U", ( 0.0, 0.56, 1.0, 1.0), 1.42, 1.42, 1.00 , 4 , 0.97 , 6 , 0.80 ),
120 (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 ),
121 (94, "Plutonium", "Pu", ( 0.0, 0.41, 1.0, 1.0), 1.00, 1.00, 1.00 , 3 , 1.08 , 4 , 0.93 ),
122 (95, "Americium", "Am", ( 0.32, 0.36, 0.94, 1.0), 1.00, 1.00, 1.00 , 3 , 1.07 , 4 , 0.92 ),
123 (96, "Curium", "Cm", ( 0.47, 0.36, 0.89, 1.0), 1.00, 1.00, 1.00 ),
124 (97, "Berkelium", "Bk", ( 0.54, 0.30, 0.89, 1.0), 1.00, 1.00, 1.00 ),
125 (98, "Californium", "Cf", ( 0.63, 0.21, 0.83, 1.0), 1.00, 1.00, 1.00 ),
126 (99, "Einsteinium", "Es", ( 0.70, 0.12, 0.83, 1.0), 1.00, 1.00, 1.00 ),
127 (100, "Fermium", "Fm", ( 0.70, 0.12, 0.72, 1.0), 1.00, 1.00, 1.00 ),
128 (101, "Mendelevium", "Md", ( 0.70, 0.05, 0.65, 1.0), 1.00, 1.00, 1.00 ),
129 (102, "Nobelium", "No", ( 0.74, 0.05, 0.52, 1.0), 1.00, 1.00, 1.00 ),
130 (103, "Lawrencium", "Lr", ( 0.78, 0.0, 0.4, 1.0), 1.00, 1.00, 1.00 ),
131 (104, "Vacancy", "Vac", ( 0.5, 0.5, 0.5, 1.0), 1.00, 1.00, 1.00),
132 (105, "Default", "Default", ( 1.0, 1.0, 1.0, 1.0), 1.00, 1.00, 1.00),
133 (106, "Stick", "Stick", ( 0.5, 0.5, 0.5, 1.0), 1.00, 1.00, 1.00),
136 # The list 'ELEMENTS' contains all data of the elements and will be used during
137 # runtime. The list will be initialized with the fixed
138 # data from above via the class below (ElementProp). One fixed list (above),
139 # which cannot be changed, and a list of classes with same data (ELEMENTS) exist.
140 # The list 'ELEMENTS' can be modified by e.g. loading a separate custom
145 # This is the class, which stores the properties for one element.
146 class ElementProp(object):
147 __slots__
= ('number',
166 self
.short_name
= short_name
169 self
.radii_ionic
= radii_ionic
170 self
.mat_P_BSDF
= mat_P_BSDF
171 self
.mat_Eevee
= mat_Eevee
174 class PBSDFProp(object):
175 __slots__
= ('Subsurface_method',
180 'Specular_ior_level',
184 'Anisotropic_rotation',
190 'Transmission_weight',
204 Anisotropic_rotation
,
214 self
.Subsurface_method
= Subsurface_method
215 self
.Distribution
= Distribution
216 self
.Subsurface_weight
= Subsurface_weight
217 self
.Subsurface_radius
= Subsurface_radius
218 self
.Metallic
= Metallic
219 self
.Specular_ior_level
= Specular_ior_level
220 self
.Specular_tint
= Specular_tint
221 self
.Roughness
= Roughness
222 self
.Anisotropic
= Anisotropic
223 self
.Anisotropic_rotation
= Anisotropic_rotation
224 self
.Sheen_weight
= Sheen_weight
225 self
.Sheen_tint
= Sheen_tint
226 self
.Coat_weight
= Coat_weight
227 self
.Coat_roughness
= Coat_roughness
229 self
.Transmission_weight
= Transmission_weight
230 self
.Emission
= Emission
231 self
.Emission_strength
= Emission_strength
235 class EeveeProp(object):
236 __slots__
= ('use_backface',
240 'use_screen_refraction',
242 'use_sss_translucency',
249 use_screen_refraction
,
251 use_sss_translucency
,
253 self
.use_backface
= use_backface
254 self
.blend_method
= blend_method
255 self
.shadow_method
= shadow_method
256 self
.clip_threshold
= clip_threshold
257 self
.use_screen_refraction
= use_screen_refraction
258 self
.refraction_depth
= refraction_depth
259 self
.use_sss_translucency
= use_sss_translucency
260 self
.pass_index
= pass_index
263 # This function measures the distance between two selected objects.
267 if bpy
.context
.mode
== 'EDIT_MESH':
269 atom
= bpy
.context
.edit_object
270 bm
= bmesh
.from_edit_mesh(atom
.data
)
275 locations
.append(atom
.matrix_world
@ v
.co
)
277 if len(locations
) > 1:
278 location1
= locations
[0]
279 location2
= locations
[1]
282 # In the 'OBJECT' mode
285 if len(bpy
.context
.selected_objects
) > 1:
286 location1
= bpy
.context
.selected_objects
[0].location
287 location2
= bpy
.context
.selected_objects
[1].location
291 dv
= location2
- location1
292 dist
= str(dv
.length
)
293 pos
= str.find(dist
, ".")
300 def choose_objects(action_type
,
307 # Note all selected objects first.
308 change_objects_all
= []
309 for atom
in bpy
.context
.selected_objects
:
310 change_objects_all
.append(atom
)
312 # This is very important now: If there are dupliverts structures, note
313 # only the parents and NOT the children! Otherwise the double work is
314 # done or the system can even crash if objects are deleted. - The
315 # chidlren are accessed anyways (see below).
317 for atom
in change_objects_all
:
318 if atom
.parent
!= None:
320 for atom2
in change_objects
:
321 if atom2
== atom
.parent
:
324 change_objects
.append(atom
)
326 change_objects
.append(atom
)
328 # And now, consider all objects, which are in the list 'change_objects'.
329 for atom
in change_objects
:
330 if len(atom
.children
) != 0:
331 for atom_child
in atom
.children
:
332 if atom_child
.type in {'SURFACE', 'MESH', 'META'}:
333 modify_objects(action_type
,
341 if atom
.type in {'SURFACE', 'MESH', 'META'}:
342 modify_objects(action_type
,
351 # Modifying the radius of a selected atom or stick
352 def modify_objects(action_type
,
360 # Modify atom radius (in pm)
361 if action_type
== "ATOM_RADIUS_PM" and "STICK" not in atom
.name
.upper():
362 if radius_pm
[0] in atom
.name
:
363 atom
.scale
= (radius_pm
[1]/100,) * 3
365 # Modify atom radius (all selected)
366 if action_type
== "ATOM_RADIUS_ALL" and "STICK" not in atom
.name
.upper():
367 atom
.scale
*= radius_all
369 # Modify atom radius (type, van der Waals, atomic or ionic)
370 if action_type
== "ATOM_RADIUS_TYPE" and "STICK" not in atom
.name
.upper():
371 for element
in ELEMENTS
:
372 if element
.name
in atom
.name
:
374 if radius_type
== '3':
375 charge_states
= element
.radii_ionic
[::2]
376 charge_radii
= element
.radii_ionic
[1::2]
377 charge_state_chosen
= int(radius_type_ionic
) - 4
379 find
= (lambda searchList
, elem
:
380 [[i
for i
, x
in enumerate(searchList
) if x
== e
]
382 index
= find(charge_states
,[charge_state_chosen
])[0]
384 # Is there a charge state?
386 atom
.scale
= (charge_radii
[index
[0]],) * 3
388 # For atomic and van der Waals radii.
390 atom
.scale
= (element
.radii
[int(radius_type
)],) * 3
393 if (action_type
== "STICKS_RADIUS_ALL" and 'STICK' in atom
.name
.upper() and
394 ('CUP' in atom
.name
.upper() or
395 'CYLINDER' in atom
.name
.upper())):
397 # For dupliverts structures only: Make the cylinder or cup visible
398 # first, otherwise one cannot go into EDIT mode. Note that 'atom' here
399 # is in fact a 'stick' (cylinder or cup).
400 # First, identify if it is a normal cylinder object or a dupliverts
401 # structure. The identifier for a dupliverts structure is the parent's
402 # name, which includes "_sticks_mesh"
403 if "_sticks_mesh" in atom
.parent
.name
:
406 bpy
.context
.view_layer
.objects
.active
= atom
407 bpy
.ops
.object.mode_set(mode
='EDIT', toggle
=False)
408 bm
= bmesh
.from_edit_mesh(atom
.data
)
412 locations
.append(v
.co
)
414 center
= Vector((0.0,0.0,0.0))
415 center
= sum([location
for location
in locations
], center
)/len(locations
)
417 radius
= sum([(loc
[0]-center
[0])**2+(loc
[1]-center
[1])**2
418 for loc
in locations
], 0)
419 radius_new
= radius
* sticks_all
422 v
.co
[0] = ((v
.co
[0] - center
[0]) / radius
) * radius_new
+ center
[0]
423 v
.co
[1] = ((v
.co
[1] - center
[1]) / radius
) * radius_new
+ center
[1]
425 bpy
.ops
.object.mode_set(mode
='OBJECT', toggle
=False)
426 # Hide again the representative stick (cylinder or cup) if it is a
427 # dupliverts structure.
428 if "_sticks_mesh" in atom
.parent
.name
:
431 bpy
.context
.view_layer
.objects
.active
= None
433 # Change the atom objects
434 if action_type
== "ATOM_REPLACE_OBJ" and "STICK" not in atom
.name
.upper():
436 scn
= bpy
.context
.scene
.atom_blend
438 material
= atom
.active_material
439 new_material
= draw_obj_material(scn
.replace_objs_material
, material
)
441 # Special object (like halo, etc.)
442 if scn
.replace_objs_special
!= '0':
443 atom
= draw_obj_special(scn
.replace_objs_special
, atom
)
444 # Standard geometrical objects
446 # If the atom shape shall not be changed, then:
447 if scn
.replace_objs
== '0':
448 atom
.active_material
= new_material
449 # If the atom shape shall change, then:
451 atom
= draw_obj(scn
.replace_objs
, atom
, new_material
)
453 # Find the sticks, if present.
454 sticks_cylinder
, sticks_cup
= find_sticks_of_atom(atom
)
457 if sticks_cylinder
!= None and sticks_cup
!= None:
458 sticks_cylinder
.active_material
= new_material
459 sticks_cup
.active_material
= new_material
460 if sticks_cylinder
!= None and sticks_cup
== None:
462 if type(sticks_cylinder
) == list:
463 for stick
in sticks_cylinder
:
464 stick
.active_material
= new_material
467 sticks_cylinder
.active_material
= new_material
469 # If the atom is the representative ball of a dupliverts structure,
470 # then make it invisible.
471 if atom
.parent
!= None:
474 # Default shape and colors for atoms
475 if action_type
== "ATOM_DEFAULT_OBJ" and "STICK" not in atom
.name
.upper():
477 scn
= bpy
.context
.scene
.atom_blend
479 # We first obtain the element form the list of elements.
480 for element
in ELEMENTS
:
481 if element
.name
in atom
.name
:
484 # Create now a new material with normal properties. Note that the
485 # 'normal material' initially used during the import could have been
486 # deleted by the user. This is why we create a new one.
487 if "vacancy" in atom
.name
.lower():
488 new_material
= draw_obj_material('2', atom
.active_material
)
490 new_material
= draw_obj_material('1', atom
.active_material
)
491 # Assign now the correct color.
492 mat_P_BSDF
= next(n
for n
in new_material
.node_tree
.nodes
493 if n
.type == "BSDF_PRINCIPLED")
494 mat_P_BSDF
.inputs
['Base Color'].default_value
= element
.color
495 new_material
.name
= element
.name
+ "_normal"
497 # Create a new atom because the current atom might have any kind
498 # of shape. For this, we use a definition from below since it also
499 # deletes the old atom.
500 if "vacancy" in atom
.name
.lower():
501 new_atom
= draw_obj('2', atom
, new_material
)
503 new_atom
= draw_obj('1b', atom
, new_material
)
505 # Now assign the material properties, name and size.
506 new_atom
.active_material
= new_material
507 new_atom
.name
= element
.name
+ "_ball"
508 new_atom
.scale
= (element
.radii
[0],) * 3
510 # Find the sticks, if present.
511 sticks_cylinder
, sticks_cup
= find_sticks_of_atom(new_atom
)
514 if sticks_cylinder
!= None and sticks_cup
!= None:
515 sticks_cylinder
.active_material
= new_material
516 sticks_cup
.active_material
= new_material
517 if sticks_cylinder
!= None and sticks_cup
== None:
519 if type(sticks_cylinder
) == list:
520 for stick
in sticks_cylinder
:
521 stick
.active_material
= new_material
524 sticks_cylinder
.active_material
= new_material
527 # Separating atoms from a dupliverts structure.
528 def separate_atoms(scn
):
531 mesh
= bpy
.context
.edit_object
533 # Do nothing if it is not a dupliverts structure.
534 if not mesh
.instance_type
== "VERTS":
537 # This is the name of the mesh
538 mesh_name
= mesh
.name
540 coll
= mesh
.users_collection
[0]
542 # Get the coordinates of the selected vertices (atoms)
543 bm
= bmesh
.from_edit_mesh(mesh
.data
)
547 locations
.append(mesh
.matrix_world
@ v
.co
)
552 # Delete already the selected vertices
553 bpy
.ops
.mesh
.delete(type='VERT')
555 # Find the representative ball within the collection.
556 for obj
in coll
.objects
:
557 if obj
.parent
!= None:
558 if obj
.parent
.name
== mesh_name
:
561 # Create balls and put them at the places where the vertices (atoms) have
563 for location
in locations
:
564 obj_dupli
= obj
.copy()
565 obj_dupli
.data
= obj
.data
.copy()
566 obj_dupli
.parent
= None
567 coll
.objects
.link(obj_dupli
)
568 obj_dupli
.location
= location
569 obj_dupli
.name
= obj
.name
+ "_sep"
570 # Do not hide the object!
571 obj_dupli
.hide_set(False)
573 bpy
.ops
.object.mode_set(mode
='OBJECT', toggle
=False)
574 bpy
.context
.view_layer
.objects
.active
= mesh
577 # Prepare a new material
578 def draw_obj_material(material_type
, material
):
580 mat_P_BSDF_default
= next(n
for n
in material
.node_tree
.nodes
581 if n
.type == "BSDF_PRINCIPLED")
582 default_color
= mat_P_BSDF_default
.inputs
['Base Color'].default_value
584 if material_type
== '0': # Unchanged
585 material_new
= material
586 if material_type
== '1': # Normal
587 # We create again the 'normal' material. Why? It's because the old
588 # one could have been deleted by the user during the course of the
589 # user's work in Blender ... .
590 material_new
= bpy
.data
.materials
.new(material
.name
+ "_normal")
591 material_new
.use_nodes
= True
592 mat_P_BSDF
= next(n
for n
in material_new
.node_tree
.nodes
593 if n
.type == "BSDF_PRINCIPLED")
594 mat_P_BSDF
.inputs
['Base Color'].default_value
= default_color
595 mat_P_BSDF
.inputs
['Metallic'].default_value
= 0.0
596 mat_P_BSDF
.inputs
['Specular IOR Level'].default_value
= 0.5
597 mat_P_BSDF
.inputs
['Roughness'].default_value
= 0.5
598 mat_P_BSDF
.inputs
['Coat Roughness'].default_value
= 0.03
599 mat_P_BSDF
.inputs
['IOR'].default_value
= 1.45
600 mat_P_BSDF
.inputs
['Transmission Weight'].default_value
= 0.0
601 mat_P_BSDF
.inputs
['Alpha'].default_value
= 1.0
602 # Some additional stuff for eevee.
603 material_new
.blend_method
= 'OPAQUE'
604 material_new
.shadow_method
= 'OPAQUE'
605 if material_type
== '2': # Transparent
606 material_new
= bpy
.data
.materials
.new(material
.name
+ "_transparent")
607 material_new
.use_nodes
= True
608 mat_P_BSDF
= next(n
for n
in material_new
.node_tree
.nodes
609 if n
.type == "BSDF_PRINCIPLED")
610 mat_P_BSDF
.inputs
['Base Color'].default_value
= default_color
611 mat_P_BSDF
.inputs
['Metallic'].default_value
= 0.0
612 mat_P_BSDF
.inputs
['Specular IOR Level'].default_value
= 0.15
613 mat_P_BSDF
.inputs
['Roughness'].default_value
= 0.2
614 mat_P_BSDF
.inputs
['Coat Roughness'].default_value
= 0.37
615 mat_P_BSDF
.inputs
['IOR'].default_value
= 1.45
616 mat_P_BSDF
.inputs
['Transmission Weight'].default_value
= 0.8
617 mat_P_BSDF
.inputs
['Alpha'].default_value
= 0.4
618 # Some additional stuff for eevee.
619 material_new
.blend_method
= 'HASHED'
620 material_new
.shadow_method
= 'HASHED'
621 material_new
.use_backface_culling
= False
622 if material_type
== '3': # Reflecting
623 material_new
= bpy
.data
.materials
.new(material
.name
+ "_reflecting")
624 material_new
.use_nodes
= True
625 mat_P_BSDF
= next(n
for n
in material_new
.node_tree
.nodes
626 if n
.type == "BSDF_PRINCIPLED")
627 mat_P_BSDF
.inputs
['Base Color'].default_value
= default_color
628 mat_P_BSDF
.inputs
['Metallic'].default_value
= 0.7
629 mat_P_BSDF
.inputs
['Specular IOR Level'].default_value
= 0.15
630 mat_P_BSDF
.inputs
['Roughness'].default_value
= 0.1
631 mat_P_BSDF
.inputs
['Coat Roughness'].default_value
= 0.5
632 mat_P_BSDF
.inputs
['IOR'].default_value
= 0.8
633 mat_P_BSDF
.inputs
['Transmission Weight'].default_value
= 0.0
634 mat_P_BSDF
.inputs
['Alpha'].default_value
= 1.0
635 # Some additional stuff for eevee.
636 material_new
.blend_method
= 'OPAQUE'
637 material_new
.shadow_method
= 'OPAQUE'
638 if material_type
== '4': # Transparent + reflecting
639 material_new
= bpy
.data
.materials
.new(material
.name
+ "_trans+refl")
640 material_new
.use_nodes
= True
641 mat_P_BSDF
= next(n
for n
in material_new
.node_tree
.nodes
642 if n
.type == "BSDF_PRINCIPLED")
643 mat_P_BSDF
.inputs
['Base Color'].default_value
= default_color
644 mat_P_BSDF
.inputs
['Metallic'].default_value
= 0.5
645 mat_P_BSDF
.inputs
['Specular IOR Level'].default_value
= 0.15
646 mat_P_BSDF
.inputs
['Roughness'].default_value
= 0.05
647 mat_P_BSDF
.inputs
['Coat Roughness'].default_value
= 0.37
648 mat_P_BSDF
.inputs
['IOR'].default_value
= 1.45
649 mat_P_BSDF
.inputs
['Transmission Weight'].default_value
= 0.6
650 mat_P_BSDF
.inputs
['Alpha'].default_value
= 0.6
651 # Some additional stuff for eevee.
652 material_new
.blend_method
= 'HASHED'
653 material_new
.shadow_method
= 'HASHED'
654 material_new
.use_backface_culling
= False
656 # Always, when the material is changed, a new name is created. Note that
657 # this makes sense: Imagine, an other object uses the same material as the
658 # selected one. After changing the material of the selected object the old
659 # material should certainly not change and remain the same.
660 if material_type
in {'1','2','3','4'}:
661 if "_repl" in material
.name
:
662 pos
= material
.name
.rfind("_repl")
663 if material
.name
[pos
+5:].isdigit():
664 counter
= int(material
.name
[pos
+5:])
665 material_new
.name
= material
.name
[:pos
]+"_repl"+str(counter
+1)
667 material_new
.name
= material
.name
+"_repl1"
669 material_new
.name
= material
.name
+"_repl1"
670 material_new
.diffuse_color
= material
.diffuse_color
675 # Get the collection of an object.
676 def get_collection_object(obj
):
678 coll_all
= obj
.users_collection
679 if len(coll_all
) > 0:
682 coll
= bpy
.context
.scene
.collection
687 # Find the sticks of an atom.
688 def find_sticks_of_atom(atom
):
690 # Initialization of the stick objects 'cylinder' and 'cup'.
691 sticks_cylinder
= None
694 # This is for dupliverts structures.
695 if atom
.parent
!= None:
700 # Get a list of all scenes.
701 cols_scene
= [c
for c
in D
.collections
if C
.scene
.user_of_id(c
)]
703 # This is the collection where the atom is inside.
704 col_atom
= atom
.parent
.users_collection
[0]
706 # Get the parent collection of the latter collection.
707 col_parent
= [c
for c
in cols_scene
if c
.user_of_id(col_atom
)][0]
709 # Get **all** children collections inside this parent collection.
710 parent_childrens
= col_parent
.children_recursive
712 # This is for dupliverts stick structures now: for each child
714 for child
in parent_childrens
:
715 # It should not have the name of the atom collection.
716 if child
.name
!= col_atom
.name
:
717 # If the sticks are inside then ...
718 if "sticks" in child
.name
:
719 # For all objects do ...
720 for obj
in child
.objects
:
721 # If the stick objects are inside then note them.
722 if "sticks_cylinder" in obj
.name
:
723 sticks_cylinder
= obj
724 if "sticks_cup" in obj
.name
:
727 # No dupliverts stick structures found? Then lets search for
728 # 'normal' and 'skin' sticks. Such sticks are in the collection
729 # 'Sticks' of the atomic structure.
730 if sticks_cylinder
== None and sticks_cup
== None:
732 # Get the grandparent collection of the parent collection.
733 col_grandparent
= [c
for c
in cols_scene
if c
.user_of_id(col_parent
)][0]
736 list_objs
= col_grandparent
.objects
737 for obj
in list_objs
:
738 if "Sticks" in obj
.name
:
739 sticks_cylinder
= obj
743 if sticks_cylinder
== None:
744 # For each child collection do:
745 for child
in col_grandparent
.children_recursive
:
746 # If the sticks are inside then ...
747 if "Sticks_cylinders" in child
.name
:
749 for obj
in child
.objects
:
750 sticks_cylinder
.append(obj
)
753 # Return the stick objects 'cylinder' and 'cup'.
755 # Dupliverts sticks => sticks_cylinder = 1, sticks_cup = 1
756 # Skin sticks => sticks_cylinder = 1, sticks_cup = None
757 # Normal sticks => sticks_cylinder = [n], sticks_cup = None
758 return sticks_cylinder
, sticks_cup
761 # Draw an object (e.g. cube, sphere, cylinder, ...)
762 def draw_obj(atom_shape
, atom
, new_material
):
765 if atom_shape
== '0':
768 if atom_shape
== '1a': #Sphere mesh
769 bpy
.ops
.mesh
.primitive_uv_sphere_add(
774 enter_editmode
=False,
775 location
=atom
.location
,
777 if atom_shape
== '1b': #Sphere NURBS
778 bpy
.ops
.surface
.primitive_nurbs_surface_sphere_add(
780 enter_editmode
=False,
781 location
=atom
.location
,
782 rotation
=(0.0, 0.0, 0.0))
783 if atom_shape
== '2': #Cube
784 bpy
.ops
.mesh
.primitive_cube_add(
786 enter_editmode
=False,
787 location
=atom
.location
,
788 rotation
=(0.0, 0.0, 0.0))
789 if atom_shape
== '3': #Plane
790 bpy
.ops
.mesh
.primitive_plane_add(
792 enter_editmode
=False,
793 location
=atom
.location
,
794 rotation
=(0.0, 0.0, 0.0))
795 if atom_shape
== '4a': #Circle
796 bpy
.ops
.mesh
.primitive_circle_add(
801 enter_editmode
=False,
802 location
=atom
.location
,
804 if atom_shape
== '4b': #Circle NURBS
805 bpy
.ops
.surface
.primitive_nurbs_surface_circle_add(
807 enter_editmode
=False,
808 location
=atom
.location
,
810 if atom_shape
in {'5a','5b','5c','5d','5e'}: #Icosphere
811 index
= {'5a':1,'5b':2,'5c':3,'5d':4,'5e':5}
812 bpy
.ops
.mesh
.primitive_ico_sphere_add(
813 subdivisions
=int(index
[atom_shape
]),
816 enter_editmode
=False,
817 location
=atom
.location
,
819 if atom_shape
== '6a': #Cylinder
820 bpy
.ops
.mesh
.primitive_cylinder_add(
824 end_fill_type
='NGON',
826 enter_editmode
=False,
827 location
=atom
.location
,
829 if atom_shape
== '6b': #Cylinder NURBS
830 bpy
.ops
.surface
.primitive_nurbs_surface_cylinder_add(
832 enter_editmode
=False,
833 location
=atom
.location
,
835 if atom_shape
== '7': #Cone
836 bpy
.ops
.mesh
.primitive_cone_add(
841 end_fill_type
='NGON',
843 enter_editmode
=False,
844 location
=atom
.location
,
846 if atom_shape
== '8a': #Torus
847 bpy
.ops
.mesh
.primitive_torus_add(
849 location
=atom
.location
,
857 if atom_shape
== '8b': #Torus NURBS
858 bpy
.ops
.surface
.primitive_nurbs_surface_torus_add(
860 enter_editmode
=False,
861 location
=atom
.location
,
864 new_atom
= bpy
.context
.view_layer
.objects
.active
865 new_atom
.scale
= atom
.scale
+ Vector((0.0,0.0,0.0))
866 new_atom
.name
= atom
.name
867 new_atom
.select_set(True)
869 new_atom
.active_material
= new_material
871 # If it is the representative object of a duplivert structure then
872 # transfer the parent and hide the new object.
873 if atom
.parent
!= None:
874 new_atom
.parent
= atom
.parent
875 new_atom
.hide_set(True)
877 # Note the collection where the old object was placed into.
878 coll_old_atom
= get_collection_object(atom
)
880 # Note the collection where the new object was placed into.
881 coll_new_atom_past
= get_collection_object(new_atom
)
883 # If it is not the same collection then ...
884 if coll_new_atom_past
!= coll_old_atom
:
885 # Put the new object into the collection of the old object and ...
886 coll_old_atom
.objects
.link(new_atom
)
887 # ... unlink the new atom from its original collection.
888 coll_new_atom_past
.objects
.unlink(new_atom
)
890 # If necessary, remove the childrens of the old object.
891 for child
in atom
.children
:
892 bpy
.ops
.object.select_all(action
='DESELECT')
894 child
.select_set(True)
896 coll_child
= get_collection_object(child
)
897 coll_child
.objects
.unlink(child
)
898 bpy
.ops
.object.delete()
900 # Deselect everything
901 bpy
.ops
.object.select_all(action
='DESELECT')
902 # Make the old atom visible.
904 # Select the old atom.
905 atom
.select_set(True)
906 # Remove the parent if necessary.
908 # Unlink the old object from the collection.
909 coll_old_atom
.objects
.unlink(atom
)
910 # Delete the old atom
911 bpy
.ops
.object.delete()
913 #if "_F2+_center" or "_F+_center" or "_F0_center" in coll_old_atom:
914 # print("Delete the collection")
919 # Draw a special object (e.g. halo, etc. ...)
920 def draw_obj_special(atom_shape
, atom
):
922 # Note the collection where 'atom' is placed into.
923 coll_atom
= get_collection_object(atom
)
925 # Now, create a collection for the new objects
927 # Create the new collection and ...
928 coll_new
= bpy
.data
.collections
.new(coll_new
)
929 # ... link it to the collection, which contains 'atom'.
930 coll_atom
.children
.link(coll_new
)
932 # Get the color of the selected atom.
933 material
= atom
.active_material
934 mat_P_BSDF_default
= next(n
for n
in material
.node_tree
.nodes
935 if n
.type == "BSDF_PRINCIPLED")
936 default_color
= mat_P_BSDF_default
.inputs
['Base Color'].default_value
938 # Create first a cube
939 bpy
.ops
.mesh
.primitive_cube_add(align
='WORLD',
940 enter_editmode
=False,
941 location
=atom
.location
,
942 rotation
=(0.0, 0.0, 0.0))
943 cube
= bpy
.context
.view_layer
.objects
.active
944 cube
.scale
= atom
.scale
+ Vector((0.0,0.0,0.0))
945 cube
.select_set(True)
948 if atom_shape
== '1':
949 cube
.name
= atom
.name
+ "_F2+_vac"
951 # New material for this cube
952 material_new
= bpy
.data
.materials
.new(atom
.name
+ "_F2+_vac")
953 material_new
.use_nodes
= True
954 mat_P_BSDF
= next(n
for n
in material_new
.node_tree
.nodes
955 if n
.type == "BSDF_PRINCIPLED")
956 mat_P_BSDF
.inputs
['Base Color'].default_value
= default_color
957 mat_P_BSDF
.inputs
['Metallic'].default_value
= 0.7
958 mat_P_BSDF
.inputs
['Specular IOR Level'].default_value
= 0.0
959 mat_P_BSDF
.inputs
['Roughness'].default_value
= 0.65
960 mat_P_BSDF
.inputs
['Coat Roughness'].default_value
= 0.0
961 mat_P_BSDF
.inputs
['IOR'].default_value
= 1.45
962 mat_P_BSDF
.inputs
['Transmission Weight'].default_value
= 0.6
963 mat_P_BSDF
.inputs
['Alpha'].default_value
= 0.6
964 # Some additional stuff for eevee.
965 material_new
.blend_method
= 'HASHED'
966 material_new
.shadow_method
= 'HASHED'
967 material_new
.use_backface_culling
= False
968 cube
.active_material
= material_new
970 # Put a point lamp inside the defect.
971 lamp_data
= bpy
.data
.lights
.new(name
=atom
.name
+ "_F2+_lamp", type="POINT")
972 lamp_data
.distance
= atom
.scale
[0] * 2.0
973 lamp_data
.energy
= 2000.0
974 lamp_data
.color
= (0.8, 0.8, 0.8)
975 lamp
= bpy
.data
.objects
.new(atom
.name
+ "_F2+_lamp", lamp_data
)
976 lamp
.location
= Vector((0.0, 0.0, 0.0))
977 bpy
.context
.collection
.objects
.link(lamp
)
980 # The new 'atom' is the F2+ defect
983 # Note the collection where all the new objects were placed into.
984 # We use only one object, the cube
985 coll_ori
= get_collection_object(cube
)
987 # If it is not the same collection then ...
988 if coll_ori
!= coll_new
:
989 # Put all new objects into the new collection and ...
990 coll_new
.objects
.link(cube
)
991 coll_new
.objects
.link(lamp
)
992 # ... unlink them from their original collection.
993 coll_ori
.objects
.unlink(cube
)
994 coll_ori
.objects
.unlink(lamp
)
996 coll_new
.name
= atom
.name
+ "_F2+_center"
998 if atom
.parent
!= None:
999 cube
.parent
= atom
.parent
1004 if atom_shape
== '2':
1005 cube
.name
= atom
.name
+ "_F2+_vac"
1007 # New material for this cube
1008 material_new
= bpy
.data
.materials
.new(atom
.name
+ "_F2+_vac")
1009 material_new
.use_nodes
= True
1010 mat_P_BSDF
= next(n
for n
in material_new
.node_tree
.nodes
1011 if n
.type == "BSDF_PRINCIPLED")
1012 mat_P_BSDF
.inputs
['Base Color'].default_value
= [0.0, 0.0, 0.8, 1.0]
1013 mat_P_BSDF
.inputs
['Metallic'].default_value
= 0.7
1014 mat_P_BSDF
.inputs
['Specular IOR Level'].default_value
= 0.0
1015 mat_P_BSDF
.inputs
['Roughness'].default_value
= 0.65
1016 mat_P_BSDF
.inputs
['Coat Roughness'].default_value
= 0.0
1017 mat_P_BSDF
.inputs
['IOR'].default_value
= 1.45
1018 mat_P_BSDF
.inputs
['Transmission Weight'].default_value
= 0.6
1019 mat_P_BSDF
.inputs
['Alpha'].default_value
= 0.6
1020 # Some additional stuff for eevee.
1021 material_new
.blend_method
= 'HASHED'
1022 material_new
.shadow_method
= 'HASHED'
1023 material_new
.use_backface_culling
= False
1024 cube
.active_material
= material_new
1026 # Create now an electron
1027 scale
= atom
.scale
/ 10.0
1028 bpy
.ops
.surface
.primitive_nurbs_surface_sphere_add(
1030 enter_editmode
=False,
1031 location
=(0.0, 0.0, 0.0),
1032 rotation
=(0.0, 0.0, 0.0))
1033 electron
= bpy
.context
.view_layer
.objects
.active
1034 electron
.scale
= scale
1035 electron
.name
= atom
.name
+ "_F+_electron"
1036 electron
.parent
= cube
1037 # New material for the electron
1038 material_electron
= bpy
.data
.materials
.new(atom
.name
+ "_F+-center")
1039 material_electron
.use_nodes
= True
1040 mat_P_BSDF
= next(n
for n
in material_electron
.node_tree
.nodes
1041 if n
.type == "BSDF_PRINCIPLED")
1042 mat_P_BSDF
.inputs
['Base Color'].default_value
= [0.0, 0.0, 0.8, 1.0]
1043 mat_P_BSDF
.inputs
['Metallic'].default_value
= 0.8
1044 mat_P_BSDF
.inputs
['Specular IOR Level'].default_value
= 0.0
1045 mat_P_BSDF
.inputs
['Roughness'].default_value
= 0.3
1046 mat_P_BSDF
.inputs
['Coat Roughness'].default_value
= 0.0
1047 mat_P_BSDF
.inputs
['IOR'].default_value
= 1.45
1048 mat_P_BSDF
.inputs
['Transmission Weight'].default_value
= 0.6
1049 mat_P_BSDF
.inputs
['Alpha'].default_value
= 1.0
1050 # Some additional stuff for eevee.
1051 material_electron
.blend_method
= 'OPAQUE'
1052 material_electron
.shadow_method
= 'OPAQUE'
1053 material_electron
.use_backface_culling
= False
1054 electron
.active_material
= material_electron
1056 # Put a point lamp inside the electron
1057 lamp_data
= bpy
.data
.lights
.new(name
=atom
.name
+ "_F+_lamp", type="POINT")
1058 lamp_data
.distance
= atom
.scale
[0] * 2.0
1059 lamp_data
.energy
= 100000.0
1060 lamp_data
.color
= (0.0, 0.0, 0.8)
1061 lamp
= bpy
.data
.objects
.new(atom
.name
+ "_F+_lamp", lamp_data
)
1062 lamp
.location
= Vector((scale
[0]*1.5, 0.0, 0.0))
1063 bpy
.context
.collection
.objects
.link(lamp
)
1066 # The new 'atom' is the F+ defect complex + lamp
1069 # Note the collection where all the new objects were placed into.
1070 # We use only one object, the cube
1071 coll_ori
= get_collection_object(cube
)
1073 # If it is not the same collection then ...
1074 if coll_ori
!= coll_new
:
1075 # Put all new objects into the new collection and ...
1076 coll_new
.objects
.link(cube
)
1077 coll_new
.objects
.link(electron
)
1078 coll_new
.objects
.link(lamp
)
1079 # ... unlink them from their original collection.
1080 coll_ori
.objects
.unlink(cube
)
1081 coll_ori
.objects
.unlink(electron
)
1082 coll_ori
.objects
.unlink(lamp
)
1084 coll_new
.name
= atom
.name
+ "_F+_center"
1086 if atom
.parent
!= None:
1087 cube
.parent
= atom
.parent
1089 electron
.hide_set(True)
1093 if atom_shape
== '3':
1094 cube
.name
= atom
.name
+ "_F2+_vac"
1096 # New material for this cube
1097 material_new
= bpy
.data
.materials
.new(atom
.name
+ "_F2+_vac")
1098 material_new
.use_nodes
= True
1099 mat_P_BSDF
= next(n
for n
in material_new
.node_tree
.nodes
1100 if n
.type == "BSDF_PRINCIPLED")
1101 mat_P_BSDF
.inputs
['Base Color'].default_value
= [0.8, 0.0, 0.0, 1.0]
1102 mat_P_BSDF
.inputs
['Metallic'].default_value
= 0.7
1103 mat_P_BSDF
.inputs
['Specular IOR Level'].default_value
= 0.0
1104 mat_P_BSDF
.inputs
['Roughness'].default_value
= 0.65
1105 mat_P_BSDF
.inputs
['Coat Roughness'].default_value
= 0.0
1106 mat_P_BSDF
.inputs
['IOR'].default_value
= 1.45
1107 mat_P_BSDF
.inputs
['Transmission Weight'].default_value
= 0.6
1108 mat_P_BSDF
.inputs
['Alpha'].default_value
= 0.6
1109 # Some additional stuff for eevee.
1110 material_new
.blend_method
= 'HASHED'
1111 material_new
.shadow_method
= 'HASHED'
1112 material_new
.use_backface_culling
= False
1113 cube
.active_material
= material_new
1115 # Create now two electrons ... .
1116 scale
= atom
.scale
/ 10.0
1117 bpy
.ops
.surface
.primitive_nurbs_surface_sphere_add(
1119 enter_editmode
=False,
1120 location
=(scale
[0]*1.5,0.0,0.0),
1121 rotation
=(0.0, 0.0, 0.0))
1122 electron1
= bpy
.context
.view_layer
.objects
.active
1123 electron1
.scale
= scale
1124 electron1
.name
= atom
.name
+ "_F0_electron_1"
1125 electron1
.parent
= cube
1126 bpy
.ops
.surface
.primitive_nurbs_surface_sphere_add(
1128 enter_editmode
=False,
1129 location
=(-scale
[0]*1.5,0.0,0.0),
1130 rotation
=(0.0, 0.0, 0.0))
1131 electron2
= bpy
.context
.view_layer
.objects
.active
1132 electron2
.scale
= scale
1133 electron2
.name
= atom
.name
+ "_F0_electron_2"
1134 electron2
.parent
= cube
1135 # Create a new material for the two electrons.
1136 material_electron
= bpy
.data
.materials
.new(atom
.name
+ "_F0-center")
1137 material_electron
.use_nodes
= True
1138 mat_P_BSDF
= next(n
for n
in material_electron
.node_tree
.nodes
1139 if n
.type == "BSDF_PRINCIPLED")
1140 mat_P_BSDF
.inputs
['Base Color'].default_value
= [0.0, 0.0, 0.8, 1.0]
1141 mat_P_BSDF
.inputs
['Metallic'].default_value
= 0.8
1142 mat_P_BSDF
.inputs
['Specular IOR Level'].default_value
= 0.0
1143 mat_P_BSDF
.inputs
['Roughness'].default_value
= 0.3
1144 mat_P_BSDF
.inputs
['Coat Roughness'].default_value
= 0.0
1145 mat_P_BSDF
.inputs
['IOR'].default_value
= 1.45
1146 mat_P_BSDF
.inputs
['Transmission Weight'].default_value
= 0.6
1147 mat_P_BSDF
.inputs
['Alpha'].default_value
= 1.0
1148 # Some additional stuff for eevee.
1149 material_electron
.blend_method
= 'OPAQUE'
1150 material_electron
.shadow_method
= 'OPAQUE'
1151 material_electron
.use_backface_culling
= False
1152 # We assign the materials to the two electrons.
1153 electron1
.active_material
= material_electron
1154 electron2
.active_material
= material_electron
1156 # Put two point lamps inside the electrons.
1157 lamp1_data
= bpy
.data
.lights
.new(name
=atom
.name
+ "_F0_lamp_1", type="POINT")
1158 lamp1_data
.distance
= atom
.scale
[0] * 2.0
1159 lamp1_data
.energy
= 20000.0
1160 lamp1_data
.color
= (0.8, 0.0, 0.0)
1161 lamp1
= bpy
.data
.objects
.new(atom
.name
+ "_F0_lamp", lamp1_data
)
1162 lamp1
.location
= Vector((scale
[0]*1.5, 0.0, 0.0))
1163 bpy
.context
.collection
.objects
.link(lamp1
)
1165 lamp2_data
= bpy
.data
.lights
.new(name
=atom
.name
+ "_F0_lamp_2", type="POINT")
1166 lamp2_data
.distance
= atom
.scale
[0] * 2.0
1167 lamp2_data
.energy
= 20000.0
1168 lamp2_data
.color
= (0.8, 0.0, 0.0)
1169 lamp2
= bpy
.data
.objects
.new(atom
.name
+ "_F0_lamp", lamp2_data
)
1170 lamp2
.location
= Vector((-scale
[0]*1.5, 0.0, 0.0))
1171 bpy
.context
.collection
.objects
.link(lamp2
)
1174 # The new 'atom' is the F0 defect complex + lamps
1177 # Note the collection where all the new objects were placed into.
1178 # We use only one object, the cube
1179 coll_ori
= get_collection_object(cube
)
1181 # If it is not the same collection then ...
1182 if coll_ori
!= coll_new
:
1183 # Put all new objects into the collection of 'atom' and ...
1184 coll_new
.objects
.link(cube
)
1185 coll_new
.objects
.link(electron1
)
1186 coll_new
.objects
.link(electron2
)
1187 coll_new
.objects
.link(lamp1
)
1188 coll_new
.objects
.link(lamp2
)
1189 # ... unlink them from their original collection.
1190 coll_ori
.objects
.unlink(cube
)
1191 coll_ori
.objects
.unlink(electron1
)
1192 coll_ori
.objects
.unlink(electron2
)
1193 coll_ori
.objects
.unlink(lamp1
)
1194 coll_ori
.objects
.unlink(lamp2
)
1196 coll_new
.name
= atom
.name
+ "_F0_center"
1198 if atom
.parent
!= None:
1199 cube
.parent
= atom
.parent
1201 electron1
.hide_set(True)
1202 electron2
.hide_set(True)
1203 lamp1
.hide_set(True)
1204 lamp2
.hide_set(True)
1206 # Deselect everything
1207 bpy
.ops
.object.select_all(action
='DESELECT')
1208 # Make the old atom visible.
1210 # Select the old atom.
1211 atom
.select_set(True)
1212 # Remove the parent if necessary.
1214 # Unlink the old object from the collection.
1215 coll_atom
.objects
.unlink(atom
)
1216 # Delete the old atom
1217 bpy
.ops
.object.delete()
1222 # Initialization of the list 'ELEMENTS'.
1223 def read_elements():
1227 for item
in ELEMENTS_DEFAULT
:
1229 # All three radii into a list
1230 radii
= [item
[4],item
[5],item
[6]]
1231 # The handling of the ionic radii will be done later. So far, it is an
1233 radii_ionic
= item
[7:]
1235 li
= ElementProp(item
[0], item
[1], item
[2], item
[3], radii
, radii_ionic
, [], [])
1240 # Custom data file: changing color and radii by using the list 'ELEMENTS'.
1241 def custom_datafile_change_atom_props():
1243 for atom
in bpy
.context
.selected_objects
:
1246 if len(atom
.children
) != 0:
1247 child
= atom
.children
[0]
1248 if child
.type in {'SURFACE', 'MESH', 'META'}:
1249 for element
in ELEMENTS
:
1250 if element
.name
in atom
.name
:
1255 if atom
.type in {'SURFACE', 'MESH', 'META'}:
1256 for element
in ELEMENTS
:
1257 if element
.name
in atom
.name
:
1263 obj
.scale
= (e
.radii
[0],) * 3
1264 mat
= obj
.active_material
1265 mat_P_BSDF
= next(n
for n
in mat
.node_tree
.nodes
1266 if n
.type == "BSDF_PRINCIPLED")
1268 mat_P_BSDF
.inputs
['Base Color'].default_value
= e
.color
1269 mat_P_BSDF
.subsurface_method
= e
.mat_P_BSDF
.Subsurface_method
1270 mat_P_BSDF
.distribution
= e
.mat_P_BSDF
.Distribution
1271 mat_P_BSDF
.inputs
['Subsurface Weight'].default_value
= e
.mat_P_BSDF
.Subsurface_weight
1272 mat_P_BSDF
.inputs
['Subsurface Radius'].default_value
= e
.mat_P_BSDF
.Subsurface_radius
1273 mat_P_BSDF
.inputs
['Metallic'].default_value
= e
.mat_P_BSDF
.Metallic
1274 mat_P_BSDF
.inputs
['Specular IOR Level'].default_value
= e
.mat_P_BSDF
.Specular_ior_level
1275 mat_P_BSDF
.inputs
['Specular Tint'].default_value
= e
.mat_P_BSDF
.Specular_tint
1276 mat_P_BSDF
.inputs
['Roughness'].default_value
= e
.mat_P_BSDF
.Roughness
1277 mat_P_BSDF
.inputs
['Anisotropic'].default_value
= e
.mat_P_BSDF
.Anisotropic
1278 mat_P_BSDF
.inputs
['Anisotropic Rotation'].default_value
= e
.mat_P_BSDF
.Anisotropic_rotation
1279 mat_P_BSDF
.inputs
['Sheen Weight'].default_value
= e
.mat_P_BSDF
.Sheen_weight
1280 mat_P_BSDF
.inputs
['Sheen Tint'].default_value
= e
.mat_P_BSDF
.Sheen_tint
1281 mat_P_BSDF
.inputs
['Coat Weight'].default_value
= e
.mat_P_BSDF
.Coat_weight
1282 mat_P_BSDF
.inputs
['Coat Roughness'].default_value
= e
.mat_P_BSDF
.Coat_roughness
1283 mat_P_BSDF
.inputs
['IOR'].default_value
= e
.mat_P_BSDF
.IOR
1284 mat_P_BSDF
.inputs
['Transmission Weight'].default_value
= e
.mat_P_BSDF
.Transmission_weight
1285 mat_P_BSDF
.inputs
['Emission'].default_value
= e
.mat_P_BSDF
.Emission
1286 mat_P_BSDF
.inputs
['Emission Strength'].default_value
= e
.mat_P_BSDF
.Emission_strength
1287 mat_P_BSDF
.inputs
['Alpha'].default_value
= e
.mat_P_BSDF
.Alpha
1289 mat
.use_backface_culling
= e
.mat_Eevee
.use_backface
1290 mat
.blend_method
= e
.mat_Eevee
.blend_method
1291 mat
.shadow_method
= e
.mat_Eevee
.shadow_method
1292 mat
.alpha_threshold
= e
.mat_Eevee
.clip_threshold
1293 mat
.use_screen_refraction
= e
.mat_Eevee
.use_screen_refraction
1294 mat
.refraction_depth
= e
.mat_Eevee
.refraction_depth
1295 mat
.use_sss_translucency
= e
.mat_Eevee
.use_sss_translucency
1296 mat
.pass_index
= e
.mat_Eevee
.pass_index
1300 bpy
.ops
.object.select_all(action
='DESELECT')
1303 # Reading a custom data file and modifying the list 'ELEMENTS'.
1304 def custom_datafile(path_datafile
):
1306 if path_datafile
== "":
1309 path_datafile
= bpy
.path
.abspath(path_datafile
)
1311 if os
.path
.isfile(path_datafile
) == False:
1314 # The whole list gets deleted! We build it new.
1317 # Read the data file, which contains all data
1318 # (atom name, radii, colors, etc.)
1319 data_file_p
= open(path_datafile
, "r")
1321 for line
in data_file_p
:
1328 list_radii_ionic
= []
1331 if len(line
) in [0,1]:
1335 if "Number :" in line
:
1336 pos
= line
.rfind(':') + 1
1337 number
= line
[pos
:].strip()
1339 if "Name :" in line
:
1340 pos
= line
.rfind(':') + 1
1341 name
= line
[pos
:].strip()
1343 if "Short name :" in line
:
1344 pos
= line
.rfind(':') + 1
1345 short_name
= line
[pos
:].strip()
1347 if "Color :" in line
:
1348 pos
= line
.rfind(':') + 1
1349 color_value
= line
[pos
:].strip().split(',')
1350 color
= [float(color_value
[0]),
1351 float(color_value
[1]),
1352 float(color_value
[2]),
1353 float(color_value
[3])]
1355 if "Radius used :" in line
:
1356 pos
= line
.rfind(':') + 1
1357 radius_used
= float(line
[pos
:].strip())
1359 if "Radius, covalent :" in line
:
1360 pos
= line
.rfind(':') + 1
1361 radius_covalent
= float(line
[pos
:].strip())
1363 if "Radius, atomic :" in line
:
1364 pos
= line
.rfind(':') + 1
1365 radius_atomic
= float(line
[pos
:].strip())
1366 if "Charge state :" in line
:
1367 pos
= line
.rfind(':') + 1
1368 charge_state
= float(line
[pos
:].strip())
1369 line
= data_file_p
.readline()
1370 pos
= line
.rfind(':') + 1
1371 radius_ionic
= float(line
[pos
:].strip())
1372 list_radii_ionic
.append(charge_state
)
1373 list_radii_ionic
.append(radius_ionic
)
1375 # Some Principled BSDF properties
1378 if "P BSDF Subsurface method :" in line
:
1379 pos
= line
.rfind(':') + 1
1380 P_BSDF_subsurface_method
= line
[pos
:].strip()
1381 if "P BSDF Distribution :" in line
:
1382 pos
= line
.rfind(':') + 1
1383 P_BSDF_distribution
= line
[pos
:].strip()
1384 if "P BSDF Subsurface Weight :" in line
:
1385 pos
= line
.rfind(':') + 1
1386 P_BSDF_subsurface_weight
= float(line
[pos
:].strip())
1387 if "P BSDF Subsurface Radius :" in line
:
1388 pos
= line
.rfind(':') + 1
1389 radii_values
= line
[pos
:].strip().split(',')
1390 P_BSDF_subsurface_radius
= [float(color_value
[0]),
1391 float(color_value
[1]),
1392 float(color_value
[2])]
1393 if "P BSDF Metallic :" in line
:
1394 pos
= line
.rfind(':') + 1
1395 P_BSDF_metallic
= float(line
[pos
:].strip())
1396 if "P BSDF Specular IOR Level :" in line
:
1397 pos
= line
.rfind(':') + 1
1398 P_BSDF_specular_ior_level
= float(line
[pos
:].strip())
1399 if "P BSDF Specular Tint :" in line
:
1400 pos
= line
.rfind(':') + 1
1401 color_value
= line
[pos
:].strip().split(',')
1402 P_BSDF_specular_tint
= [float(color_value
[0]),
1403 float(color_value
[1]),
1404 float(color_value
[2]),
1405 float(color_value
[3])]
1406 if "P BSDF Roughness :" in line
:
1407 pos
= line
.rfind(':') + 1
1408 P_BSDF_roughness
= float(line
[pos
:].strip())
1409 if "P BSDF Anisotropic :" in line
:
1410 pos
= line
.rfind(':') + 1
1411 P_BSDF_anisotropic
= float(line
[pos
:].strip())
1412 if "P BSDF Anisotropic Rotation :" in line
:
1413 pos
= line
.rfind(':') + 1
1414 P_BSDF_anisotropic_rotation
= float(line
[pos
:].strip())
1415 if "P BSDF Sheen Weight : " in line
:
1416 pos
= line
.rfind(':') + 1
1417 P_BSDF_sheen_weight
= float(line
[pos
:].strip())
1418 if "P BSDF Sheen Tint : " in line
:
1419 pos
= line
.rfind(':') + 1
1420 P_BSDF_sheen_tint
= float(line
[pos
:].strip())
1421 if "P BSDF Coat Weight :" in line
:
1422 pos
= line
.rfind(':') + 1
1423 P_BSDF_coat_weight
= float(line
[pos
:].strip())
1424 if "P BSDF Coat Roughness :" in line
:
1425 pos
= line
.rfind(':') + 1
1426 P_BSDF_coat_roughness
= float(line
[pos
:].strip())
1427 if "P BSDF IOR :" in line
:
1428 pos
= line
.rfind(':') + 1
1429 P_BSDF_IOR
= float(line
[pos
:].strip())
1430 if "P BSDF Transmission Weight :" in line
:
1431 pos
= line
.rfind(':') + 1
1432 P_BSDF_transmission_weight
= float(line
[pos
:].strip())
1433 if "P BSDF Emisssion : " in line
:
1434 pos
= line
.rfind(':') + 1
1435 color_value
= line
[pos
:].strip().split(',')
1436 P_BSDF_emission
= [float(color_value
[0]),
1437 float(color_value
[1]),
1438 float(color_value
[2]),
1439 float(color_value
[3])]
1440 if "P BSDF Emission Strength :" in line
:
1441 pos
= line
.rfind(':') + 1
1442 P_BSDF_emission_strength
= float(line
[pos
:].strip())
1443 if "P BSDF Alpha :" in line
:
1444 pos
= line
.rfind(':') + 1
1445 P_BSDF_alpha
= float(line
[pos
:].strip())
1447 if "Eevee Use Backface Culling :" in line
:
1448 pos
= line
.rfind(':') + 1
1449 line
= line
[pos
:].strip()
1450 if line
.lower() in ("yes", "true", "1"):
1451 Eevee_use_backface
= True
1453 Eevee_use_backface
= False
1454 if "Eevee Blend Method :" in line
:
1455 pos
= line
.rfind(':') + 1
1456 Eevee_blend_method
= line
[pos
:].strip()
1457 if "Eevee Shadow Method :" in line
:
1458 pos
= line
.rfind(':') + 1
1459 Eevee_shadow_method
= line
[pos
:].strip()
1460 if "Eevee Clip Threshold :" in line
:
1461 pos
= line
.rfind(':') + 1
1462 Eevee_clip_threshold
= float(line
[pos
:].strip())
1463 if "Eevee Use Screen Refraction :" in line
:
1464 pos
= line
.rfind(':') + 1
1465 line
= line
[pos
:].strip()
1466 if line
.lower() in ("yes", "true", "1"):
1467 Eevee_use_screen_refraction
= True
1469 Eevee_use_screen_refraction
= False
1470 if "Eevee Refraction depth : " in line
:
1471 pos
= line
.rfind(':') + 1
1472 Eevee_refraction_depth
= float(line
[pos
:].strip())
1473 if "Eevee Use SSS Translucency :" in line
:
1474 pos
= line
.rfind(':') + 1
1475 line
= line
[pos
:].strip()
1476 if line
.lower() in ("yes", "true", "1"):
1477 Eevee_use_sss_translucency
= True
1479 Eevee_use_sss_translucency
= False
1480 if "Eevee Pass Index :" in line
:
1481 pos
= line
.rfind(':') + 1
1482 Eevee_pass_index
= int(line
[pos
:].strip())
1485 line
= data_file_p
.readline()
1487 list_radii
= [radius_used
, radius_covalent
, radius_atomic
]
1489 Eevee_material
= EeveeProp(Eevee_use_backface
,
1491 Eevee_shadow_method
,
1492 Eevee_clip_threshold
,
1493 Eevee_use_screen_refraction
,
1494 Eevee_refraction_depth
,
1495 Eevee_use_sss_translucency
,
1498 P_BSDF_material
= PBSDFProp(P_BSDF_subsurface_method
,
1499 P_BSDF_distribution
,
1500 P_BSDF_subsurface_weight
,
1501 P_BSDF_subsurface_radius
,
1503 P_BSDF_specular_ior_level
,
1504 P_BSDF_specular_tint
,
1507 P_BSDF_anisotropic_rotation
,
1508 P_BSDF_sheen_weight
,
1511 P_BSDF_coat_roughness
,
1513 P_BSDF_transmission_weight
,
1515 P_BSDF_emission_strength
,
1518 element
= ElementProp(number
,
1527 ELEMENTS
.append(element
)