# SPDX-License-Identifier: GPL-2.0-or-later import os import bpy import bmesh from mathutils import Vector from math import sqrt from copy import copy # ----------------------------------------------------------------------------- # Atom and element data # This is a list that contains some data of all possible elements. The structure # is as follows: # # 1, "Hydrogen", "H", [0.0,0.0,1.0], 0.32, 0.32, 0.32 , -1 , 1.54 means # # No., name, short name, color, radius (used), radius (covalent), radius (atomic), # # charge state 1, radius (ionic) 1, charge state 2, radius (ionic) 2, ... all # charge states for any atom are listed, if existing. # The list is fixed and cannot be changed ... (see below) ELEMENTS_DEFAULT = ( ( 1, "Hydrogen", "H", ( 1.0, 1.0, 1.0, 1.0), 0.32, 0.32, 0.79 , -1 , 1.54 ), ( 2, "Helium", "He", ( 0.85, 1.0, 1.0, 1.0), 0.93, 0.93, 0.49 ), ( 3, "Lithium", "Li", ( 0.8, 0.50, 1.0, 1.0), 1.23, 1.23, 2.05 , 1 , 0.68 ), ( 4, "Beryllium", "Be", ( 0.76, 1.0, 0.0, 1.0), 0.90, 0.90, 1.40 , 1 , 0.44 , 2 , 0.35 ), ( 5, "Boron", "B", ( 1.0, 0.70, 0.70, 1.0), 0.82, 0.82, 1.17 , 1 , 0.35 , 3 , 0.23 ), ( 6, "Carbon", "C", ( 0.56, 0.56, 0.56, 1.0), 0.77, 0.77, 0.91 , -4 , 2.60 , 4 , 0.16 ), ( 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 ), ( 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 ), ( 9, "Fluorine", "F", ( 0.56, 0.87, 0.31, 1.0), 0.72, 0.72, 0.57 , -1 , 1.33 , 7 , 0.08 ), (10, "Neon", "Ne", ( 0.70, 0.89, 0.96, 1.0), 0.71, 0.71, 0.51 , 1 , 1.12 ), (11, "Sodium", "Na", ( 0.67, 0.36, 0.94, 1.0), 1.54, 1.54, 2.23 , 1 , 0.97 ), (12, "Magnesium", "Mg", ( 0.54, 1.0, 0.0, 1.0), 1.36, 1.36, 1.72 , 1 , 0.82 , 2 , 0.66 ), (13, "Aluminium", "Al", ( 0.74, 0.65, 0.65, 1.0), 1.18, 1.18, 1.82 , 3 , 0.51 ), (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 ), (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 ), (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 ), (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 ), (18, "Argon", "Ar", ( 0.50, 0.81, 0.89, 1.0), 0.98, 0.98, 0.88 , 1 , 1.54 ), (19, "Potassium", "K", ( 0.56, 0.25, 0.83, 1.0), 2.03, 2.03, 2.77 , 1 , 0.81 ), (20, "Calcium", "Ca", ( 0.23, 1.0, 0.0, 1.0), 1.74, 1.74, 2.23 , 1 , 1.18 , 2 , 0.99 ), (21, "Scandium", "Sc", ( 0.90, 0.90, 0.90, 1.0), 1.44, 1.44, 2.09 , 3 , 0.73 ), (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 ), (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 ), (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 ), (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 ), (26, "Iron", "Fe", ( 0.87, 0.4, 0.2, 1.0), 1.17, 1.17, 1.72 , 2 , 0.74 , 3 , 0.64 ), (27, "Cobalt", "Co", ( 0.94, 0.56, 0.62, 1.0), 1.16, 1.16, 1.67 , 2 , 0.72 , 3 , 0.63 ), (28, "Nickel", "Ni", ( 0.31, 0.81, 0.31, 1.0), 1.15, 1.15, 1.62 , 2 , 0.69 ), (29, "Copper", "Cu", ( 0.78, 0.50, 0.2, 1.0), 1.17, 1.17, 1.57 , 1 , 0.96 , 2 , 0.72 ), (30, "Zinc", "Zn", ( 0.49, 0.50, 0.69, 1.0), 1.25, 1.25, 1.53 , 1 , 0.88 , 2 , 0.74 ), (31, "Gallium", "Ga", ( 0.76, 0.56, 0.56, 1.0), 1.26, 1.26, 1.81 , 1 , 0.81 , 3 , 0.62 ), (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 ), (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 ), (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 ), (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 ), (36, "Krypton", "Kr", ( 0.36, 0.72, 0.81, 1.0), 1.31, 1.31, 1.24 ), (37, "Rubidium", "Rb", ( 0.43, 0.18, 0.69, 1.0), 2.16, 2.16, 2.98 , 1 , 1.47 ), (38, "Strontium", "Sr", ( 0.0, 1.0, 0.0, 1.0), 1.91, 1.91, 2.45 , 2 , 1.12 ), (39, "Yttrium", "Y", ( 0.58, 1.0, 1.0, 1.0), 1.62, 1.62, 2.27 , 3 , 0.89 ), (40, "Zirconium", "Zr", ( 0.58, 0.87, 0.87, 1.0), 1.45, 1.45, 2.16 , 1 , 1.09 , 4 , 0.79 ), (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 ), (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 ), (43, "Technetium", "Tc", ( 0.23, 0.61, 0.61, 1.0), 1.27, 1.27, 1.95 , 7 , 0.97 ), (44, "Ruthenium", "Ru", ( 0.14, 0.56, 0.56, 1.0), 1.25, 1.25, 1.89 , 4 , 0.67 ), (45, "Rhodium", "Rh", ( 0.03, 0.49, 0.54, 1.0), 1.25, 1.25, 1.83 , 3 , 0.68 ), (46, "Palladium", "Pd", ( 0.0, 0.41, 0.52, 1.0), 1.28, 1.28, 1.79 , 2 , 0.80 , 4 , 0.65 ), (47, "Silver", "Ag", ( 0.75, 0.75, 0.75, 1.0), 1.34, 1.34, 1.75 , 1 , 1.26 , 2 , 0.89 ), (48, "Cadmium", "Cd", ( 1.0, 0.85, 0.56, 1.0), 1.48, 1.48, 1.71 , 1 , 1.14 , 2 , 0.97 ), (49, "Indium", "In", ( 0.65, 0.45, 0.45, 1.0), 1.44, 1.44, 2.00 , 3 , 0.81 ), (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 ), (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 ), (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 ), (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 ), (54, "Xenon", "Xe", ( 0.25, 0.61, 0.69, 1.0), 1.31, 1.31, 1.24 ), (55, "Caesium", "Cs", ( 0.34, 0.09, 0.56, 1.0), 2.35, 2.35, 3.35 , 1 , 1.67 ), (56, "Barium", "Ba", ( 0.0, 0.78, 0.0, 1.0), 1.98, 1.98, 2.78 , 1 , 1.53 , 2 , 1.34 ), (57, "Lanthanum", "La", ( 0.43, 0.83, 1.0, 1.0), 1.69, 1.69, 2.74 , 1 , 1.39 , 3 , 1.06 ), (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 ), (59, "Praseodymium", "Pr", ( 0.85, 1.0, 0.78, 1.0), 1.65, 1.65, 2.67 , 3 , 1.01 , 4 , 0.90 ), (60, "Neodymium", "Nd", ( 0.78, 1.0, 0.78, 1.0), 1.64, 1.64, 2.64 , 3 , 0.99 ), (61, "Promethium", "Pm", ( 0.63, 1.0, 0.78, 1.0), 1.63, 1.63, 2.62 , 3 , 0.97 ), (62, "Samarium", "Sm", ( 0.56, 1.0, 0.78, 1.0), 1.62, 1.62, 2.59 , 3 , 0.96 ), (63, "Europium", "Eu", ( 0.38, 1.0, 0.78, 1.0), 1.85, 1.85, 2.56 , 2 , 1.09 , 3 , 0.95 ), (64, "Gadolinium", "Gd", ( 0.27, 1.0, 0.78, 1.0), 1.61, 1.61, 2.54 , 3 , 0.93 ), (65, "Terbium", "Tb", ( 0.18, 1.0, 0.78, 1.0), 1.59, 1.59, 2.51 , 3 , 0.92 , 4 , 0.84 ), (66, "Dysprosium", "Dy", ( 0.12, 1.0, 0.78, 1.0), 1.59, 1.59, 2.49 , 3 , 0.90 ), (67, "Holmium", "Ho", ( 0.0, 1.0, 0.61, 1.0), 1.58, 1.58, 2.47 , 3 , 0.89 ), (68, "Erbium", "Er", ( 0.0, 0.90, 0.45, 1.0), 1.57, 1.57, 2.45 , 3 , 0.88 ), (69, "Thulium", "Tm", ( 0.0, 0.83, 0.32, 1.0), 1.56, 1.56, 2.42 , 3 , 0.87 ), (70, "Ytterbium", "Yb", ( 0.0, 0.74, 0.21, 1.0), 1.74, 1.74, 2.40 , 2 , 0.93 , 3 , 0.85 ), (71, "Lutetium", "Lu", ( 0.0, 0.67, 0.14, 1.0), 1.56, 1.56, 2.25 , 3 , 0.85 ), (72, "Hafnium", "Hf", ( 0.30, 0.76, 1.0, 1.0), 1.44, 1.44, 2.16 , 4 , 0.78 ), (73, "Tantalum", "Ta", ( 0.30, 0.65, 1.0, 1.0), 1.34, 1.34, 2.09 , 5 , 0.68 ), (74, "Tungsten", "W", ( 0.12, 0.58, 0.83, 1.0), 1.30, 1.30, 2.02 , 4 , 0.70 , 6 , 0.62 ), (75, "Rhenium", "Re", ( 0.14, 0.49, 0.67, 1.0), 1.28, 1.28, 1.97 , 4 , 0.72 , 7 , 0.56 ), (76, "Osmium", "Os", ( 0.14, 0.4, 0.58, 1.0), 1.26, 1.26, 1.92 , 4 , 0.88 , 6 , 0.69 ), (77, "Iridium", "Ir", ( 0.09, 0.32, 0.52, 1.0), 1.27, 1.27, 1.87 , 4 , 0.68 ), (78, "Platinum", "Pt", ( 0.81, 0.81, 0.87, 1.0), 1.30, 1.30, 1.83 , 2 , 0.80 , 4 , 0.65 ), (79, "Gold", "Au", ( 1.0, 0.81, 0.13, 1.0), 1.34, 1.34, 1.79 , 1 , 1.37 , 3 , 0.85 ), (80, "Mercury", "Hg", ( 0.72, 0.72, 0.81, 1.0), 1.49, 1.49, 1.76 , 1 , 1.27 , 2 , 1.10 ), (81, "Thallium", "Tl", ( 0.65, 0.32, 0.30, 1.0), 1.48, 1.48, 2.08 , 1 , 1.47 , 3 , 0.95 ), (82, "Lead", "Pb", ( 0.34, 0.34, 0.38, 1.0), 1.47, 1.47, 1.81 , 2 , 1.20 , 4 , 0.84 ), (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 ), (84, "Polonium", "Po", ( 0.67, 0.36, 0.0, 1.0), 1.46, 1.46, 1.53 , 6 , 0.67 ), (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 ), (86, "Radon", "Rn", ( 0.25, 0.50, 0.58, 1.0), 1.00, 1.00, 1.34 ), (87, "Francium", "Fr", ( 0.25, 0.0, 0.4, 1.0), 1.00, 1.00, 1.00 , 1 , 1.80 ), (88, "Radium", "Ra", ( 0.0, 0.49, 0.0, 1.0), 1.00, 1.00, 1.00 , 2 , 1.43 ), (89, "Actinium", "Ac", ( 0.43, 0.67, 0.98, 1.0), 1.00, 1.00, 1.00 , 3 , 1.18 ), (90, "Thorium", "Th", ( 0.0, 0.72, 1.0, 1.0), 1.65, 1.65, 1.00 , 4 , 1.02 ), (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 ), (92, "Uranium", "U", ( 0.0, 0.56, 1.0, 1.0), 1.42, 1.42, 1.00 , 4 , 0.97 , 6 , 0.80 ), (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 ), (94, "Plutonium", "Pu", ( 0.0, 0.41, 1.0, 1.0), 1.00, 1.00, 1.00 , 3 , 1.08 , 4 , 0.93 ), (95, "Americium", "Am", ( 0.32, 0.36, 0.94, 1.0), 1.00, 1.00, 1.00 , 3 , 1.07 , 4 , 0.92 ), (96, "Curium", "Cm", ( 0.47, 0.36, 0.89, 1.0), 1.00, 1.00, 1.00 ), (97, "Berkelium", "Bk", ( 0.54, 0.30, 0.89, 1.0), 1.00, 1.00, 1.00 ), (98, "Californium", "Cf", ( 0.63, 0.21, 0.83, 1.0), 1.00, 1.00, 1.00 ), (99, "Einsteinium", "Es", ( 0.70, 0.12, 0.83, 1.0), 1.00, 1.00, 1.00 ), (100, "Fermium", "Fm", ( 0.70, 0.12, 0.72, 1.0), 1.00, 1.00, 1.00 ), (101, "Mendelevium", "Md", ( 0.70, 0.05, 0.65, 1.0), 1.00, 1.00, 1.00 ), (102, "Nobelium", "No", ( 0.74, 0.05, 0.52, 1.0), 1.00, 1.00, 1.00 ), (103, "Lawrencium", "Lr", ( 0.78, 0.0, 0.4, 1.0), 1.00, 1.00, 1.00 ), (104, "Vacancy", "Vac", ( 0.5, 0.5, 0.5, 1.0), 1.00, 1.00, 1.00), (105, "Default", "Default", ( 1.0, 1.0, 1.0, 1.0), 1.00, 1.00, 1.00), (106, "Stick", "Stick", ( 0.5, 0.5, 0.5, 1.0), 1.00, 1.00, 1.00), ) # The list 'ELEMENTS' contains all data of the elements and will be used during # runtime. The list will be initialized with the fixed # data from above via the class below (ElementProp). One fixed list (above), # which cannot be changed, and a list of classes with same data (ELEMENTS) exist. # The list 'ELEMENTS' can be modified by e.g. loading a separate custom # data file. ELEMENTS = [] # This is the class, which stores the properties for one element. class ElementProp(object): __slots__ = ('number', 'name', 'short_name', 'color', 'radii', 'radii_ionic', 'mat_P_BSDF', 'mat_Eevee') def __init__(self, number, name, short_name, color, radii, radii_ionic, mat_P_BSDF, mat_Eevee): self.number = number self.name = name self.short_name = short_name self.color = color self.radii = radii self.radii_ionic = radii_ionic self.mat_P_BSDF = mat_P_BSDF self.mat_Eevee = mat_Eevee class PBSDFProp(object): __slots__ = ('Subsurface_method', 'Distribution', 'Subsurface', 'Subsurface_color', 'Subsurface_radius', 'Metallic', 'Specular', 'Specular_tilt', 'Roughness', 'Anisotropic', 'Anisotropic_rotation', 'Sheen', 'Sheen_tint', 'Clearcoat', 'Clearcoat_rough', 'IOR', 'Trans', 'Trans_rough', 'Emission', 'Emission_strength', 'Alpha') def __init__(self, Subsurface_method, Distribution, Subsurface, Subsurface_color, Subsurface_radius, Metallic, Specular, Specular_tilt, Roughness, Anisotropic, Anisotropic_rotation, Sheen, Sheen_tint, Clearcoat, Clearcoat_rough, IOR, Trans, Trans_rough, Emission, Emission_strength, Alpha): self.Subsurface_method = Subsurface_method self.Distribution = Distribution self.Subsurface = Subsurface self.Subsurface_color = Subsurface_color self.Subsurface_radius = Subsurface_radius self.Metallic = Metallic self.Specular = Specular self.Specular_tilt = Specular_tilt self.Roughness = Roughness self.Anisotropic = Anisotropic self.Anisotropic_rotation = Anisotropic_rotation self.Sheen = Sheen self.Sheen_tint = Sheen_tint self.Clearcoat = Clearcoat self.Clearcoat_rough = Clearcoat_rough self.IOR = IOR self.Trans = Trans self.Trans_rough = Trans_rough self.Emission = Emission self.Emission_strength = Emission_strength self.Alpha = Alpha class EeveeProp(object): __slots__ = ('use_backface', 'blend_method', 'shadow_method', 'clip_threshold', 'use_screen_refraction', 'refraction_depth', 'use_sss_translucency', 'pass_index') def __init__(self, use_backface, blend_method, shadow_method, clip_threshold, use_screen_refraction, refraction_depth, use_sss_translucency, pass_index): self.use_backface = use_backface self.blend_method = blend_method self.shadow_method = shadow_method self.clip_threshold = clip_threshold self.use_screen_refraction = use_screen_refraction self.refraction_depth = refraction_depth self.use_sss_translucency = use_sss_translucency self.pass_index = pass_index # This function measures the distance between two selected objects. def distance(): # In the 'EDIT' mode if bpy.context.mode == 'EDIT_MESH': atom = bpy.context.edit_object bm = bmesh.from_edit_mesh(atom.data) locations = [] for v in bm.verts: if v.select: locations.append(atom.matrix_world @ v.co) if len(locations) > 1: location1 = locations[0] location2 = locations[1] else: return "N.A" # In the 'OBJECT' mode else: if len(bpy.context.selected_objects) > 1: location1 = bpy.context.selected_objects[0].location location2 = bpy.context.selected_objects[1].location else: return "N.A." dv = location2 - location1 dist = str(dv.length) pos = str.find(dist, ".") dist = dist[:pos+4] dist = dist + " A" return dist def choose_objects(action_type, radius_all, radius_pm, radius_type, radius_type_ionic, sticks_all): # Note all selected objects first. change_objects_all = [] for atom in bpy.context.selected_objects: change_objects_all.append(atom) # This is very important now: If there are dupliverts structures, note # only the parents and NOT the children! Otherwise the double work is # done or the system can even crash if objects are deleted. - The # chidlren are accessed anyways (see below). change_objects = [] for atom in change_objects_all: if atom.parent != None: FLAG = False for atom2 in change_objects: if atom2 == atom.parent: FLAG = True if FLAG == False: change_objects.append(atom) else: change_objects.append(atom) # And now, consider all objects, which are in the list 'change_objects'. for atom in change_objects: if len(atom.children) != 0: for atom_child in atom.children: if atom_child.type in {'SURFACE', 'MESH', 'META'}: modify_objects(action_type, atom_child, radius_all, radius_pm, radius_type, radius_type_ionic, sticks_all) else: if atom.type in {'SURFACE', 'MESH', 'META'}: modify_objects(action_type, atom, radius_all, radius_pm, radius_type, radius_type_ionic, sticks_all) # Modifying the radius of a selected atom or stick def modify_objects(action_type, atom, radius_all, radius_pm, radius_type, radius_type_ionic, sticks_all): # Modify atom radius (in pm) if action_type == "ATOM_RADIUS_PM" and "STICK" not in atom.name.upper(): if radius_pm[0] in atom.name: atom.scale = (radius_pm[1]/100,) * 3 # Modify atom radius (all selected) if action_type == "ATOM_RADIUS_ALL" and "STICK" not in atom.name.upper(): atom.scale *= radius_all # Modify atom radius (type, van der Waals, atomic or ionic) if action_type == "ATOM_RADIUS_TYPE" and "STICK" not in atom.name.upper(): for element in ELEMENTS: if element.name in atom.name: # For ionic radii if radius_type == '3': charge_states = element.radii_ionic[::2] charge_radii = element.radii_ionic[1::2] charge_state_chosen = int(radius_type_ionic) - 4 find = (lambda searchList, elem: [[i for i, x in enumerate(searchList) if x == e] for e in elem]) index = find(charge_states,[charge_state_chosen])[0] # Is there a charge state? if index != []: atom.scale = (charge_radii[index[0]],) * 3 # For atomic and van der Waals radii. else: atom.scale = (element.radii[int(radius_type)],) * 3 # Modify atom sticks if (action_type == "STICKS_RADIUS_ALL" and 'STICK' in atom.name.upper() and ('CUP' in atom.name.upper() or 'CYLINDER' in atom.name.upper())): # For dupliverts structures only: Make the cylinder or cup visible # first, otherwise one cannot go into EDIT mode. Note that 'atom' here # is in fact a 'stick' (cylinder or cup). # First, identify if it is a normal cylinder object or a dupliverts # structure. The identifier for a dupliverts structure is the parent's # name, which includes "_sticks_mesh" if "_sticks_mesh" in atom.parent.name: atom.hide_set(False) bpy.context.view_layer.objects.active = atom bpy.ops.object.mode_set(mode='EDIT', toggle=False) bm = bmesh.from_edit_mesh(atom.data) locations = [] for v in bm.verts: locations.append(v.co) center = Vector((0.0,0.0,0.0)) center = sum([location for location in locations], center)/len(locations) radius = sum([(loc[0]-center[0])**2+(loc[1]-center[1])**2 for loc in locations], 0) radius_new = radius * sticks_all for v in bm.verts: v.co[0] = ((v.co[0] - center[0]) / radius) * radius_new + center[0] v.co[1] = ((v.co[1] - center[1]) / radius) * radius_new + center[1] bpy.ops.object.mode_set(mode='OBJECT', toggle=False) # Hide again the representative stick (cylinder or cup) if it is a # dupliverts structure. if "_sticks_mesh" in atom.parent.name: atom.hide_set(True) bpy.context.view_layer.objects.active = None # Change the atom objects if action_type == "ATOM_REPLACE_OBJ" and "STICK" not in atom.name.upper(): scn = bpy.context.scene.atom_blend material = atom.active_material new_material = draw_obj_material(scn.replace_objs_material, material) # Special object (like halo, etc.) if scn.replace_objs_special != '0': atom = draw_obj_special(scn.replace_objs_special, atom) # Standard geometrical objects else: # If the atom shape shall not be changed, then: if scn.replace_objs == '0': atom.active_material = new_material # If the atom shape shall change, then: else: atom = draw_obj(scn.replace_objs, atom, new_material) # Find the sticks, if present. sticks_cylinder, sticks_cup = find_sticks_of_atom(atom) # Dupliverts sticks if sticks_cylinder != None and sticks_cup != None: sticks_cylinder.active_material = new_material sticks_cup.active_material = new_material if sticks_cylinder != None and sticks_cup == None: # Normal sticks if type(sticks_cylinder) == list: for stick in sticks_cylinder: stick.active_material = new_material # Skin sticks else: sticks_cylinder.active_material = new_material # If the atom is the representative ball of a dupliverts structure, # then make it invisible. if atom.parent != None: atom.hide_set(True) # Default shape and colors for atoms if action_type == "ATOM_DEFAULT_OBJ" and "STICK" not in atom.name.upper(): scn = bpy.context.scene.atom_blend # We first obtain the element form the list of elements. for element in ELEMENTS: if element.name in atom.name: break # Create now a new material with normal properties. Note that the # 'normal material' initially used during the import could have been # deleted by the user. This is why we create a new one. if "vacancy" in atom.name.lower(): new_material = draw_obj_material('2', atom.active_material) else: new_material = draw_obj_material('1', atom.active_material) # Assign now the correct color. mat_P_BSDF = new_material.node_tree.nodes['Principled BSDF'] mat_P_BSDF.inputs['Base Color'].default_value = element.color new_material.name = element.name + "_normal" # Create a new atom because the current atom might have any kind # of shape. For this, we use a definition from below since it also # deletes the old atom. if "vacancy" in atom.name.lower(): new_atom = draw_obj('2', atom, new_material) else: new_atom = draw_obj('1b', atom, new_material) # Now assign the material properties, name and size. new_atom.active_material = new_material new_atom.name = element.name + "_ball" new_atom.scale = (element.radii[0],) * 3 # Find the sticks, if present. sticks_cylinder, sticks_cup = find_sticks_of_atom(new_atom) # Dupliverts sticks if sticks_cylinder != None and sticks_cup != None: sticks_cylinder.active_material = new_material sticks_cup.active_material = new_material if sticks_cylinder != None and sticks_cup == None: # Normal sticks if type(sticks_cylinder) == list: for stick in sticks_cylinder: stick.active_material = new_material # Skin sticks else: sticks_cylinder.active_material = new_material # Separating atoms from a dupliverts structure. def separate_atoms(scn): # Get the mesh. mesh = bpy.context.edit_object # Do nothing if it is not a dupliverts structure. if not mesh.instance_type == "VERTS": return {'FINISHED'} # This is the name of the mesh mesh_name = mesh.name # Get the collection coll = mesh.users_collection[0] # Get the coordinates of the selected vertices (atoms) bm = bmesh.from_edit_mesh(mesh.data) locations = [] for v in bm.verts: if v.select: locations.append(mesh.matrix_world @ v.co) # Free memory bm.free() # Delete already the selected vertices bpy.ops.mesh.delete(type='VERT') # Find the representative ball within the collection. for obj in coll.objects: if obj.parent != None: if obj.parent.name == mesh_name: break # Create balls and put them at the places where the vertices (atoms) have # been before. for location in locations: obj_dupli = obj.copy() obj_dupli.data = obj.data.copy() obj_dupli.parent = None coll.objects.link(obj_dupli) obj_dupli.location = location obj_dupli.name = obj.name + "_sep" # Do not hide the object! obj_dupli.hide_set(False) bpy.ops.object.mode_set(mode='OBJECT', toggle=False) bpy.context.view_layer.objects.active = mesh # Prepare a new material def draw_obj_material(material_type, material): mat_P_BSDF_default = material.node_tree.nodes['Principled BSDF'] default_color = mat_P_BSDF_default.inputs['Base Color'].default_value if material_type == '0': # Unchanged material_new = material if material_type == '1': # Normal # We create again the 'normal' material. Why? It's because the old # one could have been deleted by the user during the course of the # user's work in Blender ... . material_new = bpy.data.materials.new(material.name + "_normal") material_new.use_nodes = True mat_P_BSDF = material_new.node_tree.nodes['Principled BSDF'] mat_P_BSDF.inputs['Base Color'].default_value = default_color mat_P_BSDF.inputs['Metallic'].default_value = 0.0 mat_P_BSDF.inputs['Specular'].default_value = 0.5 mat_P_BSDF.inputs['Roughness'].default_value = 0.5 mat_P_BSDF.inputs['Clearcoat Roughness'].default_value = 0.03 mat_P_BSDF.inputs['IOR'].default_value = 1.45 mat_P_BSDF.inputs['Transmission'].default_value = 0.0 mat_P_BSDF.inputs['Transmission Roughness'].default_value = 0.0 mat_P_BSDF.inputs['Alpha'].default_value = 1.0 # Some additional stuff for eevee. material_new.blend_method = 'OPAQUE' material_new.shadow_method = 'OPAQUE' if material_type == '2': # Transparent material_new = bpy.data.materials.new(material.name + "_transparent") material_new.use_nodes = True mat_P_BSDF = material_new.node_tree.nodes['Principled BSDF'] mat_P_BSDF.inputs['Base Color'].default_value = default_color mat_P_BSDF.inputs['Metallic'].default_value = 0.0 mat_P_BSDF.inputs['Specular'].default_value = 0.15 mat_P_BSDF.inputs['Roughness'].default_value = 0.2 mat_P_BSDF.inputs['Clearcoat Roughness'].default_value = 0.37 mat_P_BSDF.inputs['IOR'].default_value = 1.45 mat_P_BSDF.inputs['Transmission'].default_value = 0.8 mat_P_BSDF.inputs['Transmission Roughness'].default_value = 0.0 mat_P_BSDF.inputs['Alpha'].default_value = 0.4 # Some additional stuff for eevee. material_new.blend_method = 'HASHED' material_new.shadow_method = 'HASHED' material_new.use_backface_culling = False if material_type == '3': # Reflecting material_new = bpy.data.materials.new(material.name + "_reflecting") material_new.use_nodes = True mat_P_BSDF = material_new.node_tree.nodes['Principled BSDF'] mat_P_BSDF.inputs['Base Color'].default_value = default_color mat_P_BSDF.inputs['Metallic'].default_value = 0.7 mat_P_BSDF.inputs['Specular'].default_value = 0.15 mat_P_BSDF.inputs['Roughness'].default_value = 0.1 mat_P_BSDF.inputs['Clearcoat Roughness'].default_value = 0.5 mat_P_BSDF.inputs['IOR'].default_value = 0.8 mat_P_BSDF.inputs['Transmission'].default_value = 0.0 mat_P_BSDF.inputs['Transmission Roughness'].default_value = 0.0 mat_P_BSDF.inputs['Alpha'].default_value = 1.0 # Some additional stuff for eevee. material_new.blend_method = 'OPAQUE' material_new.shadow_method = 'OPAQUE' if material_type == '4': # Transparent + reflecting material_new = bpy.data.materials.new(material.name + "_trans+refl") material_new.use_nodes = True mat_P_BSDF = material_new.node_tree.nodes['Principled BSDF'] mat_P_BSDF.inputs['Base Color'].default_value = default_color mat_P_BSDF.inputs['Metallic'].default_value = 0.5 mat_P_BSDF.inputs['Specular'].default_value = 0.15 mat_P_BSDF.inputs['Roughness'].default_value = 0.05 mat_P_BSDF.inputs['Clearcoat Roughness'].default_value = 0.37 mat_P_BSDF.inputs['IOR'].default_value = 1.45 mat_P_BSDF.inputs['Transmission'].default_value = 0.6 mat_P_BSDF.inputs['Transmission Roughness'].default_value = 0.0 mat_P_BSDF.inputs['Alpha'].default_value = 0.6 # Some additional stuff for eevee. material_new.blend_method = 'HASHED' material_new.shadow_method = 'HASHED' material_new.use_backface_culling = False # Always, when the material is changed, a new name is created. Note that # this makes sense: Imagine, an other object uses the same material as the # selected one. After changing the material of the selected object the old # material should certainly not change and remain the same. if material_type in {'1','2','3','4'}: if "_repl" in material.name: pos = material.name.rfind("_repl") if material.name[pos+5:].isdigit(): counter = int(material.name[pos+5:]) material_new.name = material.name[:pos]+"_repl"+str(counter+1) else: material_new.name = material.name+"_repl1" else: material_new.name = material.name+"_repl1" material_new.diffuse_color = material.diffuse_color return material_new # Get the collection of an object. def get_collection_object(obj): coll_all = obj.users_collection if len(coll_all) > 0: coll = coll_all[0] else: coll = bpy.context.scene.collection return coll # Find the sticks of an atom. def find_sticks_of_atom(atom): # Initialization of the stick objects 'cylinder' and 'cup'. sticks_cylinder = None sticks_cup = None # This is for dupliverts structures. if atom.parent != None: D = bpy.data C = bpy.context # Get a list of all scenes. cols_scene = [c for c in D.collections if C.scene.user_of_id(c)] # This is the collection where the atom is inside. col_atom = atom.parent.users_collection[0] # Get the parent collection of the latter collection. col_parent = [c for c in cols_scene if c.user_of_id(col_atom)][0] # Get **all** children collections inside this parent collection. parent_childrens = col_parent.children_recursive # This is for dupliverts stick structures now: for each child # collection do: for child in parent_childrens: # It should not have the name of the atom collection. if child.name != col_atom.name: # If the sticks are inside then ... if "sticks" in child.name: # For all objects do ... for obj in child.objects: # If the stick objects are inside then note them. if "sticks_cylinder" in obj.name: sticks_cylinder = obj if "sticks_cup" in obj.name: sticks_cup = obj # No dupliverts stick structures found? Then lets search for # 'normal' and 'skin' sticks. Such sticks are in the collection # 'Sticks' of the atomic structure. if sticks_cylinder == None and sticks_cup == None: # Get the grandparent collection of the parent collection. col_grandparent = [c for c in cols_scene if c.user_of_id(col_parent)][0] # Skin sticks: list_objs = col_grandparent.objects for obj in list_objs: if "Sticks" in obj.name: sticks_cylinder = obj break # Normal sticks if sticks_cylinder == None: # For each child collection do: for child in col_grandparent.children_recursive: # If the sticks are inside then ... if "Sticks_cylinders" in child.name: sticks_cylinder = [] for obj in child.objects: sticks_cylinder.append(obj) break # Return the stick objects 'cylinder' and 'cup'. # # Dupliverts sticks => sticks_cylinder = 1, sticks_cup = 1 # Skin sticks => sticks_cylinder = 1, sticks_cup = None # Normal sticks => sticks_cylinder = [n], sticks_cup = None return sticks_cylinder, sticks_cup # Draw an object (e.g. cube, sphere, cylinder, ...) def draw_obj(atom_shape, atom, new_material): # No change if atom_shape == '0': return None if atom_shape == '1a': #Sphere mesh bpy.ops.mesh.primitive_uv_sphere_add( segments=32, ring_count=32, radius=1, align='WORLD', enter_editmode=False, location=atom.location, rotation=(0, 0, 0)) if atom_shape == '1b': #Sphere NURBS bpy.ops.surface.primitive_nurbs_surface_sphere_add( align='WORLD', enter_editmode=False, location=atom.location, rotation=(0.0, 0.0, 0.0)) if atom_shape == '2': #Cube bpy.ops.mesh.primitive_cube_add( align='WORLD', enter_editmode=False, location=atom.location, rotation=(0.0, 0.0, 0.0)) if atom_shape == '3': #Plane bpy.ops.mesh.primitive_plane_add( align='WORLD', enter_editmode=False, location=atom.location, rotation=(0.0, 0.0, 0.0)) if atom_shape == '4a': #Circle bpy.ops.mesh.primitive_circle_add( vertices=32, radius=1, fill_type='NOTHING', align='WORLD', enter_editmode=False, location=atom.location, rotation=(0, 0, 0)) if atom_shape == '4b': #Circle NURBS bpy.ops.surface.primitive_nurbs_surface_circle_add( align='WORLD', enter_editmode=False, location=atom.location, rotation=(0, 0, 0)) if atom_shape in {'5a','5b','5c','5d','5e'}: #Icosphere index = {'5a':1,'5b':2,'5c':3,'5d':4,'5e':5} bpy.ops.mesh.primitive_ico_sphere_add( subdivisions=int(index[atom_shape]), radius=1, align='WORLD', enter_editmode=False, location=atom.location, rotation=(0, 0, 0)) if atom_shape == '6a': #Cylinder bpy.ops.mesh.primitive_cylinder_add( vertices=32, radius=1, depth=2, end_fill_type='NGON', align='WORLD', enter_editmode=False, location=atom.location, rotation=(0, 0, 0)) if atom_shape == '6b': #Cylinder NURBS bpy.ops.surface.primitive_nurbs_surface_cylinder_add( align='WORLD', enter_editmode=False, location=atom.location, rotation=(0, 0, 0)) if atom_shape == '7': #Cone bpy.ops.mesh.primitive_cone_add( vertices=32, radius1=1, radius2=0, depth=2, end_fill_type='NGON', align='WORLD', enter_editmode=False, location=atom.location, rotation=(0, 0, 0)) if atom_shape == '8a': #Torus bpy.ops.mesh.primitive_torus_add( rotation=(0, 0, 0), location=atom.location, align='WORLD', major_radius=1, minor_radius=0.25, major_segments=48, minor_segments=12, abso_major_rad=1, abso_minor_rad=0.5) if atom_shape == '8b': #Torus NURBS bpy.ops.surface.primitive_nurbs_surface_torus_add( align='WORLD', enter_editmode=False, location=atom.location, rotation=(0, 0, 0)) new_atom = bpy.context.view_layer.objects.active new_atom.scale = atom.scale + Vector((0.0,0.0,0.0)) new_atom.name = atom.name new_atom.select_set(True) new_atom.active_material = new_material # If it is the representative object of a duplivert structure then # transfer the parent and hide the new object. if atom.parent != None: new_atom.parent = atom.parent new_atom.hide_set(True) # Note the collection where the old object was placed into. coll_old_atom = get_collection_object(atom) # Note the collection where the new object was placed into. coll_new_atom_past = get_collection_object(new_atom) # If it is not the same collection then ... if coll_new_atom_past != coll_old_atom: # Put the new object into the collection of the old object and ... coll_old_atom.objects.link(new_atom) # ... unlink the new atom from its original collection. coll_new_atom_past.objects.unlink(new_atom) # If necessary, remove the childrens of the old object. for child in atom.children: bpy.ops.object.select_all(action='DESELECT') child.hide_set(True) child.select_set(True) child.parent = None coll_child = get_collection_object(child) coll_child.objects.unlink(child) bpy.ops.object.delete() # Deselect everything bpy.ops.object.select_all(action='DESELECT') # Make the old atom visible. atom.hide_set(True) # Select the old atom. atom.select_set(True) # Remove the parent if necessary. atom.parent = None # Unlink the old object from the collection. coll_old_atom.objects.unlink(atom) # Delete the old atom bpy.ops.object.delete() #if "_F2+_center" or "_F+_center" or "_F0_center" in coll_old_atom: # print("Delete the collection") return new_atom # Draw a special object (e.g. halo, etc. ...) def draw_obj_special(atom_shape, atom): # Note the collection where 'atom' is placed into. coll_atom = get_collection_object(atom) # Now, create a collection for the new objects coll_new = atom.name # Create the new collection and ... coll_new = bpy.data.collections.new(coll_new) # ... link it to the collection, which contains 'atom'. coll_atom.children.link(coll_new) # Get the color of the selected atom. material = atom.active_material mat_P_BSDF_default = material.node_tree.nodes['Principled BSDF'] default_color = mat_P_BSDF_default.inputs['Base Color'].default_value # Create first a cube bpy.ops.mesh.primitive_cube_add(align='WORLD', enter_editmode=False, location=atom.location, rotation=(0.0, 0.0, 0.0)) cube = bpy.context.view_layer.objects.active cube.scale = atom.scale + Vector((0.0,0.0,0.0)) cube.select_set(True) # F2+ center if atom_shape == '1': cube.name = atom.name + "_F2+_vac" # New material for this cube material_new = bpy.data.materials.new(atom.name + "_F2+_vac") material_new.use_nodes = True mat_P_BSDF = material_new.node_tree.nodes['Principled BSDF'] mat_P_BSDF.inputs['Base Color'].default_value = default_color mat_P_BSDF.inputs['Metallic'].default_value = 0.7 mat_P_BSDF.inputs['Specular'].default_value = 0.0 mat_P_BSDF.inputs['Roughness'].default_value = 0.65 mat_P_BSDF.inputs['Clearcoat Roughness'].default_value = 0.0 mat_P_BSDF.inputs['IOR'].default_value = 1.45 mat_P_BSDF.inputs['Transmission'].default_value = 0.6 mat_P_BSDF.inputs['Transmission Roughness'].default_value = 0.5 mat_P_BSDF.inputs['Alpha'].default_value = 0.6 # Some additional stuff for eevee. material_new.blend_method = 'HASHED' material_new.shadow_method = 'HASHED' material_new.use_backface_culling = False cube.active_material = material_new # Put a point lamp inside the defect. lamp_data = bpy.data.lights.new(name=atom.name + "_F2+_lamp", type="POINT") lamp_data.distance = atom.scale[0] * 2.0 lamp_data.energy = 2000.0 lamp_data.color = (0.8, 0.8, 0.8) lamp = bpy.data.objects.new(atom.name + "_F2+_lamp", lamp_data) lamp.location = Vector((0.0, 0.0, 0.0)) bpy.context.collection.objects.link(lamp) lamp.parent = cube # The new 'atom' is the F2+ defect new_atom = cube # Note the collection where all the new objects were placed into. # We use only one object, the cube coll_ori = get_collection_object(cube) # If it is not the same collection then ... if coll_ori != coll_new: # Put all new objects into the new collection and ... coll_new.objects.link(cube) coll_new.objects.link(lamp) # ... unlink them from their original collection. coll_ori.objects.unlink(cube) coll_ori.objects.unlink(lamp) coll_new.name = atom.name + "_F2+_center" if atom.parent != None: cube.parent = atom.parent cube.hide_set(True) lamp.hide_set(True) # F+ center if atom_shape == '2': cube.name = atom.name + "_F2+_vac" # New material for this cube material_new = bpy.data.materials.new(atom.name + "_F2+_vac") material_new.use_nodes = True mat_P_BSDF = material_new.node_tree.nodes['Principled BSDF'] mat_P_BSDF.inputs['Base Color'].default_value = [0.0, 0.0, 0.8, 1.0] mat_P_BSDF.inputs['Metallic'].default_value = 0.7 mat_P_BSDF.inputs['Specular'].default_value = 0.0 mat_P_BSDF.inputs['Roughness'].default_value = 0.65 mat_P_BSDF.inputs['Clearcoat Roughness'].default_value = 0.0 mat_P_BSDF.inputs['IOR'].default_value = 1.45 mat_P_BSDF.inputs['Transmission'].default_value = 0.6 mat_P_BSDF.inputs['Transmission Roughness'].default_value = 0.5 mat_P_BSDF.inputs['Alpha'].default_value = 0.6 # Some additional stuff for eevee. material_new.blend_method = 'HASHED' material_new.shadow_method = 'HASHED' material_new.use_backface_culling = False cube.active_material = material_new # Create now an electron scale = atom.scale / 10.0 bpy.ops.surface.primitive_nurbs_surface_sphere_add( align='WORLD', enter_editmode=False, location=(0.0, 0.0, 0.0), rotation=(0.0, 0.0, 0.0)) electron = bpy.context.view_layer.objects.active electron.scale = scale electron.name = atom.name + "_F+_electron" electron.parent = cube # New material for the electron material_electron = bpy.data.materials.new(atom.name + "_F+-center") material_electron.use_nodes = True mat_P_BSDF = material_electron.node_tree.nodes['Principled BSDF'] mat_P_BSDF.inputs['Base Color'].default_value = [0.0, 0.0, 0.8, 1.0] mat_P_BSDF.inputs['Metallic'].default_value = 0.8 mat_P_BSDF.inputs['Specular'].default_value = 0.0 mat_P_BSDF.inputs['Roughness'].default_value = 0.3 mat_P_BSDF.inputs['Clearcoat Roughness'].default_value = 0.0 mat_P_BSDF.inputs['IOR'].default_value = 1.45 mat_P_BSDF.inputs['Transmission'].default_value = 0.6 mat_P_BSDF.inputs['Transmission Roughness'].default_value = 0.5 mat_P_BSDF.inputs['Alpha'].default_value = 1.0 # Some additional stuff for eevee. material_electron.blend_method = 'OPAQUE' material_electron.shadow_method = 'OPAQUE' material_electron.use_backface_culling = False electron.active_material = material_electron # Put a point lamp inside the electron lamp_data = bpy.data.lights.new(name=atom.name + "_F+_lamp", type="POINT") lamp_data.distance = atom.scale[0] * 2.0 lamp_data.energy = 100000.0 lamp_data.color = (0.0, 0.0, 0.8) lamp = bpy.data.objects.new(atom.name + "_F+_lamp", lamp_data) lamp.location = Vector((scale[0]*1.5, 0.0, 0.0)) bpy.context.collection.objects.link(lamp) lamp.parent = cube # The new 'atom' is the F+ defect complex + lamp new_atom = cube # Note the collection where all the new objects were placed into. # We use only one object, the cube coll_ori = get_collection_object(cube) # If it is not the same collection then ... if coll_ori != coll_new: # Put all new objects into the new collection and ... coll_new.objects.link(cube) coll_new.objects.link(electron) coll_new.objects.link(lamp) # ... unlink them from their original collection. coll_ori.objects.unlink(cube) coll_ori.objects.unlink(electron) coll_ori.objects.unlink(lamp) coll_new.name = atom.name + "_F+_center" if atom.parent != None: cube.parent = atom.parent cube.hide_set(True) electron.hide_set(True) lamp.hide_set(True) # F0 center if atom_shape == '3': cube.name = atom.name + "_F2+_vac" # New material for this cube material_new = bpy.data.materials.new(atom.name + "_F2+_vac") material_new.use_nodes = True mat_P_BSDF = material_new.node_tree.nodes['Principled BSDF'] mat_P_BSDF.inputs['Base Color'].default_value = [0.8, 0.0, 0.0, 1.0] mat_P_BSDF.inputs['Metallic'].default_value = 0.7 mat_P_BSDF.inputs['Specular'].default_value = 0.0 mat_P_BSDF.inputs['Roughness'].default_value = 0.65 mat_P_BSDF.inputs['Clearcoat Roughness'].default_value = 0.0 mat_P_BSDF.inputs['IOR'].default_value = 1.45 mat_P_BSDF.inputs['Transmission'].default_value = 0.6 mat_P_BSDF.inputs['Transmission Roughness'].default_value = 0.5 mat_P_BSDF.inputs['Alpha'].default_value = 0.6 # Some additional stuff for eevee. material_new.blend_method = 'HASHED' material_new.shadow_method = 'HASHED' material_new.use_backface_culling = False cube.active_material = material_new # Create now two electrons ... . scale = atom.scale / 10.0 bpy.ops.surface.primitive_nurbs_surface_sphere_add( align='WORLD', enter_editmode=False, location=(scale[0]*1.5,0.0,0.0), rotation=(0.0, 0.0, 0.0)) electron1 = bpy.context.view_layer.objects.active electron1.scale = scale electron1.name = atom.name + "_F0_electron_1" electron1.parent = cube bpy.ops.surface.primitive_nurbs_surface_sphere_add( align='WORLD', enter_editmode=False, location=(-scale[0]*1.5,0.0,0.0), rotation=(0.0, 0.0, 0.0)) electron2 = bpy.context.view_layer.objects.active electron2.scale = scale electron2.name = atom.name + "_F0_electron_2" electron2.parent = cube # Create a new material for the two electrons. material_electron = bpy.data.materials.new(atom.name + "_F0-center") material_electron.use_nodes = True mat_P_BSDF = material_electron.node_tree.nodes['Principled BSDF'] mat_P_BSDF.inputs['Base Color'].default_value = [0.0, 0.0, 0.8, 1.0] mat_P_BSDF.inputs['Metallic'].default_value = 0.8 mat_P_BSDF.inputs['Specular'].default_value = 0.0 mat_P_BSDF.inputs['Roughness'].default_value = 0.3 mat_P_BSDF.inputs['Clearcoat Roughness'].default_value = 0.0 mat_P_BSDF.inputs['IOR'].default_value = 1.45 mat_P_BSDF.inputs['Transmission'].default_value = 0.6 mat_P_BSDF.inputs['Transmission Roughness'].default_value = 0.5 mat_P_BSDF.inputs['Alpha'].default_value = 1.0 # Some additional stuff for eevee. material_electron.blend_method = 'OPAQUE' material_electron.shadow_method = 'OPAQUE' material_electron.use_backface_culling = False # We assign the materials to the two electrons. electron1.active_material = material_electron electron2.active_material = material_electron # Put two point lamps inside the electrons. lamp1_data = bpy.data.lights.new(name=atom.name + "_F0_lamp_1", type="POINT") lamp1_data.distance = atom.scale[0] * 2.0 lamp1_data.energy = 20000.0 lamp1_data.color = (0.8, 0.0, 0.0) lamp1 = bpy.data.objects.new(atom.name + "_F0_lamp", lamp1_data) lamp1.location = Vector((scale[0]*1.5, 0.0, 0.0)) bpy.context.collection.objects.link(lamp1) lamp1.parent = cube lamp2_data = bpy.data.lights.new(name=atom.name + "_F0_lamp_2", type="POINT") lamp2_data.distance = atom.scale[0] * 2.0 lamp2_data.energy = 20000.0 lamp2_data.color = (0.8, 0.0, 0.0) lamp2 = bpy.data.objects.new(atom.name + "_F0_lamp", lamp2_data) lamp2.location = Vector((-scale[0]*1.5, 0.0, 0.0)) bpy.context.collection.objects.link(lamp2) lamp2.parent = cube # The new 'atom' is the F0 defect complex + lamps new_atom = cube # Note the collection where all the new objects were placed into. # We use only one object, the cube coll_ori = get_collection_object(cube) # If it is not the same collection then ... if coll_ori != coll_new: # Put all new objects into the collection of 'atom' and ... coll_new.objects.link(cube) coll_new.objects.link(electron1) coll_new.objects.link(electron2) coll_new.objects.link(lamp1) coll_new.objects.link(lamp2) # ... unlink them from their original collection. coll_ori.objects.unlink(cube) coll_ori.objects.unlink(electron1) coll_ori.objects.unlink(electron2) coll_ori.objects.unlink(lamp1) coll_ori.objects.unlink(lamp2) coll_new.name = atom.name + "_F0_center" if atom.parent != None: cube.parent = atom.parent cube.hide_set(True) electron1.hide_set(True) electron2.hide_set(True) lamp1.hide_set(True) lamp2.hide_set(True) # Deselect everything bpy.ops.object.select_all(action='DESELECT') # Make the old atom visible. atom.hide_set(True) # Select the old atom. atom.select_set(True) # Remove the parent if necessary. atom.parent = None # Unlink the old object from the collection. coll_atom.objects.unlink(atom) # Delete the old atom bpy.ops.object.delete() return new_atom # Initialization of the list 'ELEMENTS'. def read_elements(): del ELEMENTS[:] for item in ELEMENTS_DEFAULT: # All three radii into a list radii = [item[4],item[5],item[6]] # The handling of the ionic radii will be done later. So far, it is an # empty list. radii_ionic = item[7:] li = ElementProp(item[0], item[1], item[2], item[3], radii, radii_ionic, [], []) ELEMENTS.append(li) # Custom data file: changing color and radii by using the list 'ELEMENTS'. def custom_datafile_change_atom_props(): for atom in bpy.context.selected_objects: FLAG = False if len(atom.children) != 0: child = atom.children[0] if child.type in {'SURFACE', 'MESH', 'META'}: for element in ELEMENTS: if element.name in atom.name: obj = child e = element FLAG = True else: if atom.type in {'SURFACE', 'MESH', 'META'}: for element in ELEMENTS: if element.name in atom.name: obj = atom e = element FLAG = True if FLAG: obj.scale = (e.radii[0],) * 3 mat = obj.active_material mat_P_BSDF = mat.node_tree.nodes['Principled BSDF'] mat_P_BSDF.inputs['Base Color'].default_value = e.color mat_P_BSDF.subsurface_method = e.mat_P_BSDF.Subsurface_method mat_P_BSDF.distribution = e.mat_P_BSDF.Distribution mat_P_BSDF.inputs['Subsurface'].default_value = e.mat_P_BSDF.Subsurface mat_P_BSDF.inputs['Subsurface Color'].default_value = e.mat_P_BSDF.Subsurface_color mat_P_BSDF.inputs['Subsurface Radius'].default_value = e.mat_P_BSDF.Subsurface_radius mat_P_BSDF.inputs['Metallic'].default_value = e.mat_P_BSDF.Metallic mat_P_BSDF.inputs['Specular'].default_value = e.mat_P_BSDF.Specular mat_P_BSDF.inputs['Specular Tint'].default_value = e.mat_P_BSDF.Specular_tilt mat_P_BSDF.inputs['Roughness'].default_value = e.mat_P_BSDF.Roughness mat_P_BSDF.inputs['Anisotropic'].default_value = e.mat_P_BSDF.Anisotropic mat_P_BSDF.inputs['Anisotropic Rotation'].default_value = e.mat_P_BSDF.Anisotropic_rotation mat_P_BSDF.inputs['Sheen'].default_value = e.mat_P_BSDF.Sheen mat_P_BSDF.inputs['Sheen Tint'].default_value = e.mat_P_BSDF.Sheen_tint mat_P_BSDF.inputs['Clearcoat'].default_value = e.mat_P_BSDF.Clearcoat mat_P_BSDF.inputs['Clearcoat Roughness'].default_value = e.mat_P_BSDF.Clearcoat_rough mat_P_BSDF.inputs['IOR'].default_value = e.mat_P_BSDF.IOR mat_P_BSDF.inputs['Transmission'].default_value = e.mat_P_BSDF.Trans mat_P_BSDF.inputs['Transmission Roughness'].default_value = e.mat_P_BSDF.Trans_rough mat_P_BSDF.inputs['Emission'].default_value = e.mat_P_BSDF.Emission mat_P_BSDF.inputs['Emission Strength'].default_value = e.mat_P_BSDF.Emission_strength mat_P_BSDF.inputs['Alpha'].default_value = e.mat_P_BSDF.Alpha mat.use_backface_culling = e.mat_Eevee.use_backface mat.blend_method = e.mat_Eevee.blend_method mat.shadow_method = e.mat_Eevee.shadow_method mat.alpha_threshold = e.mat_Eevee.clip_threshold mat.use_screen_refraction = e.mat_Eevee.use_screen_refraction mat.refraction_depth = e.mat_Eevee.refraction_depth mat.use_sss_translucency = e.mat_Eevee.use_sss_translucency mat.pass_index = e.mat_Eevee.pass_index FLAG = False bpy.ops.object.select_all(action='DESELECT') # Reading a custom data file and modifying the list 'ELEMENTS'. def custom_datafile(path_datafile): if path_datafile == "": return False path_datafile = bpy.path.abspath(path_datafile) if os.path.isfile(path_datafile) == False: return False # The whole list gets deleted! We build it new. del ELEMENTS[:] # Read the data file, which contains all data # (atom name, radii, colors, etc.) data_file_p = open(path_datafile, "r") for line in data_file_p: if "#" == line[0]: continue if "Atom" in line: list_radii_ionic = [] while True: if len(line) in [0,1]: break # Number if "Number :" in line: pos = line.rfind(':') + 1 number = line[pos:].strip() # Name if "Name :" in line: pos = line.rfind(':') + 1 name = line[pos:].strip() # Short name if "Short name :" in line: pos = line.rfind(':') + 1 short_name = line[pos:].strip() # Color if "Color :" in line: pos = line.rfind(':') + 1 color_value = line[pos:].strip().split(',') color = [float(color_value[0]), float(color_value[1]), float(color_value[2]), float(color_value[3])] # Used radius if "Radius used :" in line: pos = line.rfind(':') + 1 radius_used = float(line[pos:].strip()) # Covalent radius if "Radius, covalent :" in line: pos = line.rfind(':') + 1 radius_covalent = float(line[pos:].strip()) # Atomic radius if "Radius, atomic :" in line: pos = line.rfind(':') + 1 radius_atomic = float(line[pos:].strip()) if "Charge state :" in line: pos = line.rfind(':') + 1 charge_state = float(line[pos:].strip()) line = data_file_p.readline() pos = line.rfind(':') + 1 radius_ionic = float(line[pos:].strip()) list_radii_ionic.append(charge_state) list_radii_ionic.append(radius_ionic) # Some Principled BSDF properties if "P BSDF Subsurface method :" in line: pos = line.rfind(':') + 1 P_BSDF_subsurface_method = line[pos:].strip() if "P BSDF Distribution :" in line: pos = line.rfind(':') + 1 P_BSDF_distribution = line[pos:].strip() if "P BSDF Subsurface :" in line: pos = line.rfind(':') + 1 P_BSDF_subsurface = float(line[pos:].strip()) if "P BSDF Subsurface Color :" in line: pos = line.rfind(':') + 1 color_value = line[pos:].strip().split(',') P_BSDF_subsurface_color = [float(color_value[0]), float(color_value[1]), float(color_value[2]), float(color_value[3])] if "P BSDF Subsurface Radius :" in line: pos = line.rfind(':') + 1 radii_values = line[pos:].strip().split(',') P_BSDF_subsurface_radius = [float(color_value[0]), float(color_value[1]), float(color_value[2])] if "P BSDF Metallic :" in line: pos = line.rfind(':') + 1 P_BSDF_metallic = float(line[pos:].strip()) if "P BSDF Specular :" in line: pos = line.rfind(':') + 1 P_BSDF_specular = float(line[pos:].strip()) if "P BSDF Specular Tilt :" in line: pos = line.rfind(':') + 1 P_BSDF_specular_tilt = float(line[pos:].strip()) if "P BSDF Roughness :" in line: pos = line.rfind(':') + 1 P_BSDF_roughness = float(line[pos:].strip()) if "P BSDF Anisotropic :" in line: pos = line.rfind(':') + 1 P_BSDF_anisotropic = float(line[pos:].strip()) if "P BSDF Anisotropic Rotation :" in line: pos = line.rfind(':') + 1 P_BSDF_anisotropic_rotation = float(line[pos:].strip()) if "P BSDF Sheen : " in line: pos = line.rfind(':') + 1 P_BSDF_sheen = float(line[pos:].strip()) if "P BSDF Sheen Tint : " in line: pos = line.rfind(':') + 1 P_BSDF_sheen_tint = float(line[pos:].strip()) if "P BSDF Clearcoat :" in line: pos = line.rfind(':') + 1 P_BSDF_clearcoat = float(line[pos:].strip()) if "P BSDF Clearcoat Rough :" in line: pos = line.rfind(':') + 1 P_BSDF_clearcoat_roughness = float(line[pos:].strip()) if "P BSDF IOR :" in line: pos = line.rfind(':') + 1 P_BSDF_IOR = float(line[pos:].strip()) if "P BSDF Trans :" in line: pos = line.rfind(':') + 1 P_BSDF_transparency = float(line[pos:].strip()) if "P BSDF Trans Roughness :" in line: pos = line.rfind(':') + 1 P_BSDF_transparency_roughness = float(line[pos:].strip()) if "P BSDF Emisssion : " in line: pos = line.rfind(':') + 1 color_value = line[pos:].strip().split(',') P_BSDF_emission = [float(color_value[0]), float(color_value[1]), float(color_value[2]), float(color_value[3])] if "P BSDF Emission Strength :" in line: pos = line.rfind(':') + 1 P_BSDF_emission_strength = float(line[pos:].strip()) if "P BSDF Alpha :" in line: pos = line.rfind(':') + 1 P_BSDF_alpha = float(line[pos:].strip()) if "Eevee Use Backface Culling :" in line: pos = line.rfind(':') + 1 line = line[pos:].strip() if line.lower() in ("yes", "true", "1"): Eevee_use_backface = True else: Eevee_use_backface = False if "Eevee Blend Method :" in line: pos = line.rfind(':') + 1 Eevee_blend_method = line[pos:].strip() if "Eevee Shadow Method :" in line: pos = line.rfind(':') + 1 Eevee_shadow_method = line[pos:].strip() if "Eevee Clip Threshold :" in line: pos = line.rfind(':') + 1 Eevee_clip_threshold = float(line[pos:].strip()) if "Eevee Use Screen Refraction :" in line: pos = line.rfind(':') + 1 line = line[pos:].strip() if line.lower() in ("yes", "true", "1"): Eevee_use_screen_refraction = True else: Eevee_use_screen_refraction = False if "Eevee Refraction depth : " in line: pos = line.rfind(':') + 1 Eevee_refraction_depth = float(line[pos:].strip()) if "Eevee Use SSS Translucency :" in line: pos = line.rfind(':') + 1 line = line[pos:].strip() if line.lower() in ("yes", "true", "1"): Eevee_use_sss_translucency = True else: Eevee_use_sss_translucency = False if "Eevee Pass Index :" in line: pos = line.rfind(':') + 1 Eevee_pass_index = int(line[pos:].strip()) line = data_file_p.readline() list_radii = [radius_used, radius_covalent, radius_atomic] Eevee_material = EeveeProp(Eevee_use_backface, Eevee_blend_method, Eevee_shadow_method, Eevee_clip_threshold, Eevee_use_screen_refraction, Eevee_refraction_depth, Eevee_use_sss_translucency, Eevee_pass_index) P_BSDF_material = PBSDFProp(P_BSDF_subsurface_method, P_BSDF_distribution, P_BSDF_subsurface, P_BSDF_subsurface_color, P_BSDF_subsurface_radius, P_BSDF_metallic, P_BSDF_specular, P_BSDF_specular_tilt, P_BSDF_roughness, P_BSDF_anisotropic, P_BSDF_anisotropic_rotation, P_BSDF_sheen, P_BSDF_sheen_tint, P_BSDF_clearcoat, P_BSDF_clearcoat_roughness, P_BSDF_IOR, P_BSDF_transparency, P_BSDF_transparency_roughness, P_BSDF_emission, P_BSDF_emission_strength, P_BSDF_alpha) element = ElementProp(number, name, short_name, color, list_radii, list_radii_ionic, P_BSDF_material, Eevee_material) ELEMENTS.append(element) data_file_p.close() return True