From 9791bd3a2233ace2d8a588c4f8b7fe6cb4c8fa7e Mon Sep 17 00:00:00 2001 From: Clemens Barth Date: Thu, 28 Mar 2019 10:37:49 +0100 Subject: =?UTF-8?q?The=20new=20=E2=80=98Atomic=20Blender=20PDB/XYZ?= =?UTF-8?q?=E2=80=99=20importer.=20T62804?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Comments ======== This is the fusion of the 3 atomic blender addons from Blender 2.79: 1. PDB (I/O addon for .pdb files, was in trunk before) 2. XYZ (I/O addon for .xyz files, was in contrib before) 3. Utilities (panel for modifying atomic structures, was in contrib before), into one single addon called ‘Atomic Blender PDB/XYZ’. --- io_mesh_atomic/__init__.py | 219 ++++ io_mesh_atomic/atom_info.dat | 2172 +++++++++++++++++++++++++++++++++++++++ io_mesh_atomic/pdb_export.py | 81 ++ io_mesh_atomic/pdb_gui.py | 270 +++++ io_mesh_atomic/pdb_import.py | 1549 ++++++++++++++++++++++++++++ io_mesh_atomic/utility_gui.py | 422 ++++++++ io_mesh_atomic/utility_panel.py | 1074 +++++++++++++++++++ io_mesh_atomic/xyz_export.py | 83 ++ io_mesh_atomic/xyz_gui.py | 210 ++++ io_mesh_atomic/xyz_import.py | 810 +++++++++++++++ 10 files changed, 6890 insertions(+) create mode 100644 io_mesh_atomic/__init__.py create mode 100644 io_mesh_atomic/atom_info.dat create mode 100644 io_mesh_atomic/pdb_export.py create mode 100644 io_mesh_atomic/pdb_gui.py create mode 100644 io_mesh_atomic/pdb_import.py create mode 100644 io_mesh_atomic/utility_gui.py create mode 100644 io_mesh_atomic/utility_panel.py create mode 100644 io_mesh_atomic/xyz_export.py create mode 100644 io_mesh_atomic/xyz_gui.py create mode 100644 io_mesh_atomic/xyz_import.py (limited to 'io_mesh_atomic') diff --git a/io_mesh_atomic/__init__.py b/io_mesh_atomic/__init__.py new file mode 100644 index 00000000..2b2d89d7 --- /dev/null +++ b/io_mesh_atomic/__init__.py @@ -0,0 +1,219 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### +# +# Author : Clemens Barth (Blendphys@root-1.de) +# Homepage(Wiki) : http://development.root-1.de/Atomic_Blender.php +# +# Start of project : 2011-08-31 by CB +# First publication in Blender : 2011-11-11 by CB +# Fusion of the PDB, XYZ and Panel : 2019-03-22 by CB +# Last modified : 2019-03-28 +# +# Contributing authors +# ==================== +# +# So far ... none ... . +# +# +# Acknowledgements +# ================ +# +# A big thank you to all those people who I met in particular in the IRC and +# who helped me a lot. +# +# Blender developers +# ------------------ +# Campbell Barton (ideasman) +# Brendon Murphy (meta_androcto) +# Truman Melton (?) (truman) +# Kilon Alios (kilon) +# ?? (CoDEmanX) +# Dima Glib (dairin0d) +# Peter K.H. Gragert (PKHG) +# Valter Battioli (?) (valter) +# ? (atmind) +# Ray Molenkamp (bzztploink) +# +# Other +# ----- +# Frank Palmino (Femto-St institute, Belfort-Montbéliard, France) +# ... for testing the addons and for feedback +# + +bl_info = { + "name": "Atomic Blender PDB/XYZ", + "description": "Importing atoms listed in PDB or XYZ files as balls into Blender", + "author": "Clemens Barth", + "version": (1, 8), + "blender": (2, 80, 0), + "location": "File -> Import -> PDB (.pdb) and File -> Import -> XYZ (.xyz)", + "warning": "", + "wiki_url": "https://wiki.blender.org/", + "category": "Import-Export", +} + +import bpy +from bpy.types import Operator, AddonPreferences +from bpy_extras.io_utils import ImportHelper, ExportHelper +from bpy.props import ( + StringProperty, + BoolProperty, + EnumProperty, + IntProperty, + FloatProperty, + ) + +from . import ( + pdb_gui, + xyz_gui, + utility_gui, + utility_panel + ) + +# ----------------------------------------------------------------------------- +# Preferences + +class AddonPreferences(AddonPreferences): + # This must match the addon name, use '__package__' + # when defining this in a submodule of a python package. + bl_idname = __name__ + + bool_pdb = BoolProperty( + name="PDB import/export", + default=True, + description="Import/export PDB", + ) + bool_xyz = BoolProperty( + name="XYZ import/export", + default=True, + description="Import/export XYZ", + ) + # This boolean is checked in the poll function in PANEL_PT_prepare + # (see utility.py). + bool_utility = BoolProperty( + name="Utility panel", + default=False, + description=("Panel with functionalities for modifying " \ + "atomic structures"), + ) + + def draw(self, context): + layout = self.layout + layout.label(text="Choose the importer(s) and a 'utility' panel") + layout.prop(self, "bool_pdb") + layout.prop(self, "bool_xyz") + layout.prop(self, "bool_utility") + + +# ----------------------------------------------------------------------------- +# Menu + + +# The entry into the menu 'file -> import' +def menu_func_import_pdb(self, context): + lay = self.layout + lay.operator(pdb_gui.IMPORT_OT_pdb.bl_idname,text="Protein Data Bank (.pdb)") + +def menu_func_import_xyz(self, context): + lay = self.layout + lay.operator(xyz_gui.IMPORT_OT_xyz.bl_idname,text="XYZ (.xyz)") + +# The entry into the menu 'file -> export' +def menu_func_export_pdb(self, context): + lay = self.layout + lay.operator(pdb_gui.EXPORT_OT_pdb.bl_idname,text="Protein Data Bank (.pdb)") + +def menu_func_export_xyz(self, context): + lay = self.layout + lay.operator(xyz_gui.EXPORT_OT_xyz.bl_idname,text="XYZ (.xyz)") + + +# ----------------------------------------------------------------------------- +# Register + +def register(): + from bpy.utils import register_class + + register_class(AddonPreferences) + + register_class(pdb_gui.IMPORT_OT_pdb) + register_class(pdb_gui.EXPORT_OT_pdb) + bpy.types.TOPBAR_MT_file_import.append(menu_func_import_pdb) + bpy.types.TOPBAR_MT_file_export.append(menu_func_export_pdb) + + register_class(xyz_gui.IMPORT_OT_xyz) + register_class(xyz_gui.EXPORT_OT_xyz) + bpy.types.TOPBAR_MT_file_import.append(menu_func_import_xyz) + bpy.types.TOPBAR_MT_file_export.append(menu_func_export_xyz) + + classes = (utility_gui.PANEL_PT_prepare, + utility_gui.PanelProperties, + utility_gui.DatafileApply, + utility_gui.DefaultAtom, + utility_gui.ReplaceAtom, + utility_gui.SeparateAtom, + utility_gui.DistanceButton, + utility_gui.RadiusAllBiggerButton, + utility_gui.RadiusAllSmallerButton, + utility_gui.SticksAllBiggerButton, + utility_gui.SticksAllSmallerButton) + from bpy.utils import register_class + utility_panel.read_elements() + for cls in classes: + register_class(cls) + + scene = bpy.types.Scene + scene.atom_blend = bpy.props.PointerProperty(type=utility_gui.PanelProperties) + + +def unregister(): + from bpy.utils import unregister_class + + unregister_class(AddonPreferences) + + unregister_class(pdb_gui.IMPORT_OT_pdb) + unregister_class(pdb_gui.EXPORT_OT_pdb) + bpy.types.TOPBAR_MT_file_import.remove(menu_func_import_pdb) + bpy.types.TOPBAR_MT_file_export.remove(menu_func_export_pdb) + + unregister_class(xyz_gui.IMPORT_OT_xyz) + unregister_class(xyz_gui.EXPORT_OT_xyz) + bpy.types.TOPBAR_MT_file_import.remove(menu_func_import_xyz) + bpy.types.TOPBAR_MT_file_export.remove(menu_func_export_xyz) + + classes = (utility_gui.PANEL_PT_prepare, + utility_gui.PanelProperties, + utility_gui.DatafileApply, + utility_gui.DefaultAtom, + utility_gui.ReplaceAtom, + utility_gui.SeparateAtom, + utility_gui.DistanceButton, + utility_gui.RadiusAllBiggerButton, + utility_gui.RadiusAllSmallerButton, + utility_gui.SticksAllBiggerButton, + utility_gui.SticksAllSmallerButton) + for cls in classes: + unregister_class(cls) + + +# ----------------------------------------------------------------------------- +# Main + +if __name__ == "__main__": + + register() diff --git a/io_mesh_atomic/atom_info.dat b/io_mesh_atomic/atom_info.dat new file mode 100644 index 00000000..83dde4b8 --- /dev/null +++ b/io_mesh_atomic/atom_info.dat @@ -0,0 +1,2172 @@ +Atom +==== +Number : 1 +Name : Hydrogen +Short name : H +Color : 1.0,1.0,1.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 0.320000 +Radius, covalent : 0.320000 +Radius, atomic : 0.790000 +Charge state : -1 +Radius, ionic : 1.540000 + + +Atom +==== +Number : 2 +Name : Helium +Short name : He +Color : 0.850980392157,1.0,1.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 0.930000 +Radius, covalent : 0.930000 +Radius, atomic : 0.490000 + + +Atom +==== +Number : 3 +Name : Lithium +Short name : Li +Color : 0.8,0.501960784314,1.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.230000 +Radius, covalent : 1.230000 +Radius, atomic : 2.050000 +Charge state : 1 +Radius, ionic : 0.680000 + + +Atom +==== +Number : 4 +Name : Beryllium +Short name : Be +Color : 0.760784313725,1.0,0.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 0.900000 +Radius, covalent : 0.900000 +Radius, atomic : 1.400000 +Charge state : 1 +Radius, ionic : 0.440000 +Charge state : 2 +Radius, ionic : 0.350000 + + +Atom +==== +Number : 5 +Name : Boron +Short name : B +Color : 1.0,0.709803921569,0.709803921569,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 0.820000 +Radius, covalent : 0.820000 +Radius, atomic : 1.170000 +Charge state : 1 +Radius, ionic : 0.350000 +Charge state : 3 +Radius, ionic : 0.230000 + + +Atom +==== +Number : 6 +Name : Carbon +Short name : C +Color : 0.564705882353,0.564705882353,0.564705882353,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 0.770000 +Radius, covalent : 0.770000 +Radius, atomic : 0.910000 +Charge state : -4 +Radius, ionic : 2.600000 +Charge state : 4 +Radius, ionic : 0.160000 + + +Atom +==== +Number : 7 +Name : Nitrogen +Short name : N +Color : 0.188235294118,0.313725490196,0.972549019608,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 0.750000 +Radius, covalent : 0.750000 +Radius, atomic : 0.750000 +Charge state : -3 +Radius, ionic : 1.710000 +Charge state : 1 +Radius, ionic : 0.250000 +Charge state : 3 +Radius, ionic : 0.160000 +Charge state : 5 +Radius, ionic : 0.130000 + + +Atom +==== +Number : 8 +Name : Oxygen +Short name : O +Color : 1.0,0.0509803921569,0.0509803921569,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 0.730000 +Radius, covalent : 0.730000 +Radius, atomic : 0.650000 +Charge state : -2 +Radius, ionic : 1.320000 +Charge state : -1 +Radius, ionic : 1.760000 +Charge state : 1 +Radius, ionic : 0.220000 +Charge state : 6 +Radius, ionic : 0.090000 + + +Atom +==== +Number : 9 +Name : Fluorine +Short name : F +Color : 0.564705882353,0.878431372549,0.313725490196,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 0.720000 +Radius, covalent : 0.720000 +Radius, atomic : 0.570000 +Charge state : -1 +Radius, ionic : 1.330000 +Charge state : 7 +Radius, ionic : 0.080000 + + +Atom +==== +Number : 10 +Name : Neon +Short name : Ne +Color : 0.701960784314,0.890196078431,0.960784313725,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 0.710000 +Radius, covalent : 0.710000 +Radius, atomic : 0.510000 +Charge state : 1 +Radius, ionic : 1.120000 + + +Atom +==== +Number : 11 +Name : Sodium +Short name : Na +Color : 0.670588235294,0.360784313725,0.949019607843,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.540000 +Radius, covalent : 1.540000 +Radius, atomic : 2.230000 +Charge state : 1 +Radius, ionic : 0.970000 + + +Atom +==== +Number : 12 +Name : Magnesium +Short name : Mg +Color : 0.541176470588,1.0,0.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.360000 +Radius, covalent : 1.360000 +Radius, atomic : 1.720000 +Charge state : 1 +Radius, ionic : 0.820000 +Charge state : 2 +Radius, ionic : 0.660000 + + +Atom +==== +Number : 13 +Name : Aluminium +Short name : Al +Color : 0.749019607843,0.650980392157,0.650980392157,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.180000 +Radius, covalent : 1.180000 +Radius, atomic : 1.820000 +Charge state : 3 +Radius, ionic : 0.510000 + + +Atom +==== +Number : 14 +Name : Silicon +Short name : Si +Color : 0.941176470588,0.78431372549,0.627450980392,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.110000 +Radius, covalent : 1.110000 +Radius, atomic : 1.460000 +Charge state : -4 +Radius, ionic : 2.710000 +Charge state : -1 +Radius, ionic : 3.840000 +Charge state : 1 +Radius, ionic : 0.650000 +Charge state : 4 +Radius, ionic : 0.420000 + + +Atom +==== +Number : 15 +Name : Phosphorus +Short name : P +Color : 1.0,0.501960784314,0.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.060000 +Radius, covalent : 1.060000 +Radius, atomic : 1.230000 +Charge state : -3 +Radius, ionic : 2.120000 +Charge state : 3 +Radius, ionic : 0.440000 +Charge state : 5 +Radius, ionic : 0.350000 + + +Atom +==== +Number : 16 +Name : Sulfur +Short name : S +Color : 1.0,1.0,0.188235294118,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.020000 +Radius, covalent : 1.020000 +Radius, atomic : 1.090000 +Charge state : -2 +Radius, ionic : 1.840000 +Charge state : 2 +Radius, ionic : 2.190000 +Charge state : 4 +Radius, ionic : 0.370000 +Charge state : 6 +Radius, ionic : 0.300000 + + +Atom +==== +Number : 17 +Name : Chlorine +Short name : Cl +Color : 0.121568627451,0.941176470588,0.121568627451,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 0.990000 +Radius, covalent : 0.990000 +Radius, atomic : 0.970000 +Charge state : -1 +Radius, ionic : 1.810000 +Charge state : 5 +Radius, ionic : 0.340000 +Charge state : 7 +Radius, ionic : 0.270000 + + +Atom +==== +Number : 18 +Name : Argon +Short name : Ar +Color : 0.501960784314,0.819607843137,0.890196078431,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 0.980000 +Radius, covalent : 0.980000 +Radius, atomic : 0.880000 +Charge state : 1 +Radius, ionic : 1.540000 + + +Atom +==== +Number : 19 +Name : Potassium +Short name : K +Color : 0.560784313725,0.250980392157,0.83137254902,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 2.030000 +Radius, covalent : 2.030000 +Radius, atomic : 2.770000 +Charge state : 1 +Radius, ionic : 0.810000 + + +Atom +==== +Number : 20 +Name : Calcium +Short name : Ca +Color : 0.239215686275,1.0,0.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.740000 +Radius, covalent : 1.740000 +Radius, atomic : 2.230000 +Charge state : 1 +Radius, ionic : 1.180000 +Charge state : 2 +Radius, ionic : 0.990000 + + +Atom +==== +Number : 21 +Name : Scandium +Short name : Sc +Color : 0.901960784314,0.901960784314,0.901960784314,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.440000 +Radius, covalent : 1.440000 +Radius, atomic : 2.090000 +Charge state : 3 +Radius, ionic : 0.732000 + + +Atom +==== +Number : 22 +Name : Titanium +Short name : Ti +Color : 0.749019607843,0.760784313725,0.780392156863,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.320000 +Radius, covalent : 1.320000 +Radius, atomic : 2.000000 +Charge state : 1 +Radius, ionic : 0.960000 +Charge state : 2 +Radius, ionic : 0.940000 +Charge state : 3 +Radius, ionic : 0.760000 +Charge state : 4 +Radius, ionic : 0.680000 + + +Atom +==== +Number : 23 +Name : Vanadium +Short name : V +Color : 0.650980392157,0.650980392157,0.670588235294,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.220000 +Radius, covalent : 1.220000 +Radius, atomic : 1.920000 +Charge state : 2 +Radius, ionic : 0.880000 +Charge state : 3 +Radius, ionic : 0.740000 +Charge state : 4 +Radius, ionic : 0.630000 +Charge state : 5 +Radius, ionic : 0.590000 + + +Atom +==== +Number : 24 +Name : Chromium +Short name : Cr +Color : 0.541176470588,0.6,0.780392156863,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.180000 +Radius, covalent : 1.180000 +Radius, atomic : 1.850000 +Charge state : 1 +Radius, ionic : 0.810000 +Charge state : 2 +Radius, ionic : 0.890000 +Charge state : 3 +Radius, ionic : 0.630000 +Charge state : 6 +Radius, ionic : 0.520000 + + +Atom +==== +Number : 25 +Name : Manganese +Short name : Mn +Color : 0.611764705882,0.478431372549,0.780392156863,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.170000 +Radius, covalent : 1.170000 +Radius, atomic : 1.790000 +Charge state : 2 +Radius, ionic : 0.800000 +Charge state : 3 +Radius, ionic : 0.660000 +Charge state : 4 +Radius, ionic : 0.600000 +Charge state : 7 +Radius, ionic : 0.460000 + + +Atom +==== +Number : 26 +Name : Iron +Short name : Fe +Color : 0.878431372549,0.4,0.2,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.170000 +Radius, covalent : 1.170000 +Radius, atomic : 1.720000 +Charge state : 2 +Radius, ionic : 0.740000 +Charge state : 3 +Radius, ionic : 0.640000 + + +Atom +==== +Number : 27 +Name : Cobalt +Short name : Co +Color : 0.941176470588,0.564705882353,0.627450980392,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.160000 +Radius, covalent : 1.160000 +Radius, atomic : 1.670000 +Charge state : 2 +Radius, ionic : 0.720000 +Charge state : 3 +Radius, ionic : 0.630000 + + +Atom +==== +Number : 28 +Name : Nickel +Short name : Ni +Color : 0.313725490196,0.81568627451,0.313725490196,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.150000 +Radius, covalent : 1.150000 +Radius, atomic : 1.620000 +Charge state : 2 +Radius, ionic : 0.690000 + + +Atom +==== +Number : 29 +Name : Copper +Short name : Cu +Color : 0.78431372549,0.501960784314,0.2,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.170000 +Radius, covalent : 1.170000 +Radius, atomic : 1.570000 +Charge state : 1 +Radius, ionic : 0.960000 +Charge state : 2 +Radius, ionic : 0.720000 + + +Atom +==== +Number : 30 +Name : Zinc +Short name : Zn +Color : 0.490196078431,0.501960784314,0.690196078431,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.250000 +Radius, covalent : 1.250000 +Radius, atomic : 1.530000 +Charge state : 1 +Radius, ionic : 0.880000 +Charge state : 2 +Radius, ionic : 0.740000 + + +Atom +==== +Number : 31 +Name : Gallium +Short name : Ga +Color : 0.760784313725,0.560784313725,0.560784313725,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.260000 +Radius, covalent : 1.260000 +Radius, atomic : 1.810000 +Charge state : 1 +Radius, ionic : 0.810000 +Charge state : 3 +Radius, ionic : 0.620000 + + +Atom +==== +Number : 32 +Name : Germanium +Short name : Ge +Color : 0.4,0.560784313725,0.560784313725,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.220000 +Radius, covalent : 1.220000 +Radius, atomic : 1.520000 +Charge state : -4 +Radius, ionic : 2.720000 +Charge state : 2 +Radius, ionic : 0.730000 +Charge state : 4 +Radius, ionic : 0.530000 + + +Atom +==== +Number : 33 +Name : Arsenic +Short name : As +Color : 0.741176470588,0.501960784314,0.890196078431,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.200000 +Radius, covalent : 1.200000 +Radius, atomic : 1.330000 +Charge state : -3 +Radius, ionic : 2.220000 +Charge state : 3 +Radius, ionic : 0.580000 +Charge state : 5 +Radius, ionic : 0.460000 + + +Atom +==== +Number : 34 +Name : Selenium +Short name : Se +Color : 1.0,0.63137254902,0.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.160000 +Radius, covalent : 1.160000 +Radius, atomic : 1.220000 +Charge state : -2 +Radius, ionic : 1.910000 +Charge state : -1 +Radius, ionic : 2.320000 +Charge state : 1 +Radius, ionic : 0.660000 +Charge state : 4 +Radius, ionic : 0.500000 +Charge state : 6 +Radius, ionic : 0.420000 + + +Atom +==== +Number : 35 +Name : Bromine +Short name : Br +Color : 0.650980392157,0.160784313725,0.160784313725,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.140000 +Radius, covalent : 1.140000 +Radius, atomic : 1.120000 +Charge state : -1 +Radius, ionic : 1.960000 +Charge state : 5 +Radius, ionic : 0.470000 +Charge state : 7 +Radius, ionic : 0.390000 + + +Atom +==== +Number : 36 +Name : Krypton +Short name : Kr +Color : 0.360784313725,0.721568627451,0.819607843137,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.310000 +Radius, covalent : 1.310000 +Radius, atomic : 1.240000 + + +Atom +==== +Number : 37 +Name : Rubidium +Short name : Rb +Color : 0.439215686275,0.180392156863,0.690196078431,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 2.160000 +Radius, covalent : 2.160000 +Radius, atomic : 2.980000 +Charge state : 1 +Radius, ionic : 1.470000 + + +Atom +==== +Number : 38 +Name : Strontium +Short name : Sr +Color : 0.0,1.0,0.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.910000 +Radius, covalent : 1.910000 +Radius, atomic : 2.450000 +Charge state : 2 +Radius, ionic : 1.120000 + + +Atom +==== +Number : 39 +Name : Yttrium +Short name : Y +Color : 0.580392156863,1.0,1.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.620000 +Radius, covalent : 1.620000 +Radius, atomic : 2.270000 +Charge state : 3 +Radius, ionic : 0.893000 + + +Atom +==== +Number : 40 +Name : Zirconium +Short name : Zr +Color : 0.580392156863,0.878431372549,0.878431372549,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.450000 +Radius, covalent : 1.450000 +Radius, atomic : 2.160000 +Charge state : 1 +Radius, ionic : 1.090000 +Charge state : 4 +Radius, ionic : 0.790000 + + +Atom +==== +Number : 41 +Name : Niobium +Short name : Nb +Color : 0.450980392157,0.760784313725,0.788235294118,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.340000 +Radius, covalent : 1.340000 +Radius, atomic : 2.080000 +Charge state : 1 +Radius, ionic : 1.000000 +Charge state : 4 +Radius, ionic : 0.740000 +Charge state : 5 +Radius, ionic : 0.690000 + + +Atom +==== +Number : 42 +Name : Molybdenum +Short name : Mo +Color : 0.329411764706,0.709803921569,0.709803921569,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.300000 +Radius, covalent : 1.300000 +Radius, atomic : 2.010000 +Charge state : 1 +Radius, ionic : 0.930000 +Charge state : 4 +Radius, ionic : 0.700000 +Charge state : 6 +Radius, ionic : 0.620000 + + +Atom +==== +Number : 43 +Name : Technetium +Short name : Tc +Color : 0.23137254902,0.619607843137,0.619607843137,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.270000 +Radius, covalent : 1.270000 +Radius, atomic : 1.950000 +Charge state : 7 +Radius, ionic : 0.979000 + + +Atom +==== +Number : 44 +Name : Ruthenium +Short name : Ru +Color : 0.141176470588,0.560784313725,0.560784313725,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.250000 +Radius, covalent : 1.250000 +Radius, atomic : 1.890000 +Charge state : 4 +Radius, ionic : 0.670000 + + +Atom +==== +Number : 45 +Name : Rhodium +Short name : Rh +Color : 0.0392156862745,0.490196078431,0.549019607843,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.250000 +Radius, covalent : 1.250000 +Radius, atomic : 1.830000 +Charge state : 3 +Radius, ionic : 0.680000 + + +Atom +==== +Number : 46 +Name : Palladium +Short name : Pd +Color : 0.0,0.411764705882,0.521568627451,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.280000 +Radius, covalent : 1.280000 +Radius, atomic : 1.790000 +Charge state : 2 +Radius, ionic : 0.800000 +Charge state : 4 +Radius, ionic : 0.650000 + + +Atom +==== +Number : 47 +Name : Silver +Short name : Ag +Color : 0.752941176471,0.752941176471,0.752941176471,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.340000 +Radius, covalent : 1.340000 +Radius, atomic : 1.750000 +Charge state : 1 +Radius, ionic : 1.260000 +Charge state : 2 +Radius, ionic : 0.890000 + + +Atom +==== +Number : 48 +Name : Cadmium +Short name : Cd +Color : 1.0,0.850980392157,0.560784313725,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.480000 +Radius, covalent : 1.480000 +Radius, atomic : 1.710000 +Charge state : 1 +Radius, ionic : 1.140000 +Charge state : 2 +Radius, ionic : 0.970000 + + +Atom +==== +Number : 49 +Name : Indium +Short name : In +Color : 0.650980392157,0.458823529412,0.450980392157,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.440000 +Radius, covalent : 1.440000 +Radius, atomic : 2.000000 +Charge state : 3 +Radius, ionic : 0.810000 + + +Atom +==== +Number : 50 +Name : Tin +Short name : Sn +Color : 0.4,0.501960784314,0.501960784314,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.410000 +Radius, covalent : 1.410000 +Radius, atomic : 1.720000 +Charge state : -4 +Radius, ionic : 2.940000 +Charge state : -1 +Radius, ionic : 3.700000 +Charge state : 2 +Radius, ionic : 0.930000 +Charge state : 4 +Radius, ionic : 0.710000 + + +Atom +==== +Number : 51 +Name : Antimony +Short name : Sb +Color : 0.619607843137,0.388235294118,0.709803921569,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.400000 +Radius, covalent : 1.400000 +Radius, atomic : 1.530000 +Charge state : -3 +Radius, ionic : 2.450000 +Charge state : 3 +Radius, ionic : 0.760000 +Charge state : 5 +Radius, ionic : 0.620000 + + +Atom +==== +Number : 52 +Name : Tellurium +Short name : Te +Color : 0.83137254902,0.478431372549,0.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.360000 +Radius, covalent : 1.360000 +Radius, atomic : 1.420000 +Charge state : -2 +Radius, ionic : 2.110000 +Charge state : -1 +Radius, ionic : 2.500000 +Charge state : 1 +Radius, ionic : 0.820000 +Charge state : 4 +Radius, ionic : 0.700000 +Charge state : 6 +Radius, ionic : 0.560000 + + +Atom +==== +Number : 53 +Name : Iodine +Short name : I +Color : 0.580392156863,0.0,0.580392156863,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.330000 +Radius, covalent : 1.330000 +Radius, atomic : 1.320000 +Charge state : -1 +Radius, ionic : 2.200000 +Charge state : 5 +Radius, ionic : 0.620000 +Charge state : 7 +Radius, ionic : 0.500000 + + +Atom +==== +Number : 54 +Name : Xenon +Short name : Xe +Color : 0.258823529412,0.619607843137,0.690196078431,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.310000 +Radius, covalent : 1.310000 +Radius, atomic : 1.240000 + + +Atom +==== +Number : 55 +Name : Caesium +Short name : Cs +Color : 0.341176470588,0.0901960784314,0.560784313725,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 2.350000 +Radius, covalent : 2.350000 +Radius, atomic : 3.350000 +Charge state : 1 +Radius, ionic : 1.670000 + + +Atom +==== +Number : 56 +Name : Barium +Short name : Ba +Color : 0.0,0.788235294118,0.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.980000 +Radius, covalent : 1.980000 +Radius, atomic : 2.780000 +Charge state : 1 +Radius, ionic : 1.530000 +Charge state : 2 +Radius, ionic : 1.340000 + + +Atom +==== +Number : 57 +Name : Lanthanum +Short name : La +Color : 0.439215686275,0.83137254902,1.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.690000 +Radius, covalent : 1.690000 +Radius, atomic : 2.740000 +Charge state : 1 +Radius, ionic : 1.390000 +Charge state : 3 +Radius, ionic : 1.061000 + + +Atom +==== +Number : 58 +Name : Cerium +Short name : Ce +Color : 1.0,1.0,0.780392156863,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.650000 +Radius, covalent : 1.650000 +Radius, atomic : 2.700000 +Charge state : 1 +Radius, ionic : 1.270000 +Charge state : 3 +Radius, ionic : 1.034000 +Charge state : 4 +Radius, ionic : 0.920000 + + +Atom +==== +Number : 59 +Name : Praseodymium +Short name : Pr +Color : 0.850980392157,1.0,0.780392156863,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.650000 +Radius, covalent : 1.650000 +Radius, atomic : 2.670000 +Charge state : 3 +Radius, ionic : 1.013000 +Charge state : 4 +Radius, ionic : 0.900000 + + +Atom +==== +Number : 60 +Name : Neodymium +Short name : Nd +Color : 0.780392156863,1.0,0.780392156863,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.640000 +Radius, covalent : 1.640000 +Radius, atomic : 2.640000 +Charge state : 3 +Radius, ionic : 0.995000 + + +Atom +==== +Number : 61 +Name : Promethium +Short name : Pm +Color : 0.639215686275,1.0,0.780392156863,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.630000 +Radius, covalent : 1.630000 +Radius, atomic : 2.620000 +Charge state : 3 +Radius, ionic : 0.979000 + + +Atom +==== +Number : 62 +Name : Samarium +Short name : Sm +Color : 0.560784313725,1.0,0.780392156863,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.620000 +Radius, covalent : 1.620000 +Radius, atomic : 2.590000 +Charge state : 3 +Radius, ionic : 0.964000 + + +Atom +==== +Number : 63 +Name : Europium +Short name : Eu +Color : 0.380392156863,1.0,0.780392156863,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.850000 +Radius, covalent : 1.850000 +Radius, atomic : 2.560000 +Charge state : 2 +Radius, ionic : 1.090000 +Charge state : 3 +Radius, ionic : 0.950000 + + +Atom +==== +Number : 64 +Name : Gadolinium +Short name : Gd +Color : 0.270588235294,1.0,0.780392156863,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.610000 +Radius, covalent : 1.610000 +Radius, atomic : 2.540000 +Charge state : 3 +Radius, ionic : 0.938000 + + +Atom +==== +Number : 65 +Name : Terbium +Short name : Tb +Color : 0.188235294118,1.0,0.780392156863,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.590000 +Radius, covalent : 1.590000 +Radius, atomic : 2.510000 +Charge state : 3 +Radius, ionic : 0.923000 +Charge state : 4 +Radius, ionic : 0.840000 + + +Atom +==== +Number : 66 +Name : Dysprosium +Short name : Dy +Color : 0.121568627451,1.0,0.780392156863,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.590000 +Radius, covalent : 1.590000 +Radius, atomic : 2.490000 +Charge state : 3 +Radius, ionic : 0.908000 + + +Atom +==== +Number : 67 +Name : Holmium +Short name : Ho +Color : 0.0,1.0,0.611764705882,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.580000 +Radius, covalent : 1.580000 +Radius, atomic : 2.470000 +Charge state : 3 +Radius, ionic : 0.894000 + + +Atom +==== +Number : 68 +Name : Erbium +Short name : Er +Color : 0.0,0.901960784314,0.458823529412,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.570000 +Radius, covalent : 1.570000 +Radius, atomic : 2.450000 +Charge state : 3 +Radius, ionic : 0.881000 + + +Atom +==== +Number : 69 +Name : Thulium +Short name : Tm +Color : 0.0,0.83137254902,0.321568627451,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.560000 +Radius, covalent : 1.560000 +Radius, atomic : 2.420000 +Charge state : 3 +Radius, ionic : 0.870000 + + +Atom +==== +Number : 70 +Name : Ytterbium +Short name : Yb +Color : 0.0,0.749019607843,0.219607843137,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.740000 +Radius, covalent : 1.740000 +Radius, atomic : 2.400000 +Charge state : 2 +Radius, ionic : 0.930000 +Charge state : 3 +Radius, ionic : 0.858000 + + +Atom +==== +Number : 71 +Name : Lutetium +Short name : Lu +Color : 0.0,0.670588235294,0.141176470588,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.560000 +Radius, covalent : 1.560000 +Radius, atomic : 2.250000 +Charge state : 3 +Radius, ionic : 0.850000 + + +Atom +==== +Number : 72 +Name : Hafnium +Short name : Hf +Color : 0.301960784314,0.760784313725,1.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.440000 +Radius, covalent : 1.440000 +Radius, atomic : 2.160000 +Charge state : 4 +Radius, ionic : 0.780000 + + +Atom +==== +Number : 73 +Name : Tantalum +Short name : Ta +Color : 0.301960784314,0.650980392157,1.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.340000 +Radius, covalent : 1.340000 +Radius, atomic : 2.090000 +Charge state : 5 +Radius, ionic : 0.680000 + + +Atom +==== +Number : 74 +Name : Tungsten +Short name : W +Color : 0.129411764706,0.580392156863,0.839215686275,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.300000 +Radius, covalent : 1.300000 +Radius, atomic : 2.020000 +Charge state : 4 +Radius, ionic : 0.700000 +Charge state : 6 +Radius, ionic : 0.620000 + + +Atom +==== +Number : 75 +Name : Rhenium +Short name : Re +Color : 0.149019607843,0.490196078431,0.670588235294,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.280000 +Radius, covalent : 1.280000 +Radius, atomic : 1.970000 +Charge state : 4 +Radius, ionic : 0.720000 +Charge state : 7 +Radius, ionic : 0.560000 + + +Atom +==== +Number : 76 +Name : Osmium +Short name : Os +Color : 0.149019607843,0.4,0.588235294118,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.260000 +Radius, covalent : 1.260000 +Radius, atomic : 1.920000 +Charge state : 4 +Radius, ionic : 0.880000 +Charge state : 6 +Radius, ionic : 0.690000 + + +Atom +==== +Number : 77 +Name : Iridium +Short name : Ir +Color : 0.0901960784314,0.329411764706,0.529411764706,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.270000 +Radius, covalent : 1.270000 +Radius, atomic : 1.870000 +Charge state : 4 +Radius, ionic : 0.680000 + + +Atom +==== +Number : 78 +Name : Platinium +Short name : Pt +Color : 0.81568627451,0.81568627451,0.878431372549,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.300000 +Radius, covalent : 1.300000 +Radius, atomic : 1.830000 +Charge state : 2 +Radius, ionic : 0.800000 +Charge state : 4 +Radius, ionic : 0.650000 + + +Atom +==== +Number : 79 +Name : Gold +Short name : Au +Color : 1.0,0.819607843137,0.137254901961,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.340000 +Radius, covalent : 1.340000 +Radius, atomic : 1.790000 +Charge state : 1 +Radius, ionic : 1.370000 +Charge state : 3 +Radius, ionic : 0.850000 + + +Atom +==== +Number : 80 +Name : Mercury +Short name : Hg +Color : 0.721568627451,0.721568627451,0.81568627451,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.490000 +Radius, covalent : 1.490000 +Radius, atomic : 1.760000 +Charge state : 1 +Radius, ionic : 1.270000 +Charge state : 2 +Radius, ionic : 1.100000 + + +Atom +==== +Number : 81 +Name : Thallium +Short name : Tl +Color : 0.650980392157,0.329411764706,0.301960784314,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.480000 +Radius, covalent : 1.480000 +Radius, atomic : 2.080000 +Charge state : 1 +Radius, ionic : 1.470000 +Charge state : 3 +Radius, ionic : 0.950000 + + +Atom +==== +Number : 82 +Name : Lead +Short name : Pb +Color : 0.341176470588,0.349019607843,0.380392156863,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.470000 +Radius, covalent : 1.470000 +Radius, atomic : 1.810000 +Charge state : 2 +Radius, ionic : 1.200000 +Charge state : 4 +Radius, ionic : 0.840000 + + +Atom +==== +Number : 83 +Name : Bismuth +Short name : Bi +Color : 0.619607843137,0.309803921569,0.709803921569,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.460000 +Radius, covalent : 1.460000 +Radius, atomic : 1.630000 +Charge state : 1 +Radius, ionic : 0.980000 +Charge state : 3 +Radius, ionic : 0.960000 +Charge state : 5 +Radius, ionic : 0.740000 + + +Atom +==== +Number : 84 +Name : Polonium +Short name : Po +Color : 0.670588235294,0.360784313725,0.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.460000 +Radius, covalent : 1.460000 +Radius, atomic : 1.530000 +Charge state : 6 +Radius, ionic : 0.670000 + + +Atom +==== +Number : 85 +Name : Astatine +Short name : At +Color : 0.458823529412,0.309803921569,0.270588235294,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.450000 +Radius, covalent : 1.450000 +Radius, atomic : 1.430000 +Charge state : -3 +Radius, ionic : 2.220000 +Charge state : 3 +Radius, ionic : 0.850000 +Charge state : 5 +Radius, ionic : 0.460000 + + +Atom +==== +Number : 86 +Name : Radon +Short name : Rn +Color : 0.258823529412,0.509803921569,0.588235294118,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.000000 +Radius, covalent : 1.000000 +Radius, atomic : 1.340000 + + +Atom +==== +Number : 87 +Name : Francium +Short name : Fr +Color : 0.258823529412,0.0,0.4,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.000000 +Radius, covalent : 1.000000 +Radius, atomic : 1.000000 +Charge state : 1 +Radius, ionic : 1.800000 + + +Atom +==== +Number : 88 +Name : Radium +Short name : Ra +Color : 0.0,0.490196078431,0.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.000000 +Radius, covalent : 1.000000 +Radius, atomic : 1.000000 +Charge state : 2 +Radius, ionic : 1.430000 + + +Atom +==== +Number : 89 +Name : Actinium +Short name : Ac +Color : 0.439215686275,0.670588235294,0.980392156863,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.000000 +Radius, covalent : 1.000000 +Radius, atomic : 1.000000 +Charge state : 3 +Radius, ionic : 1.180000 + + +Atom +==== +Number : 90 +Name : Thorium +Short name : Th +Color : 0.0,0.729411764706,1.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.650000 +Radius, covalent : 1.650000 +Radius, atomic : 1.000000 +Charge state : 4 +Radius, ionic : 1.020000 + + +Atom +==== +Number : 91 +Name : Protactinium +Short name : Pa +Color : 0.0,0.63137254902,1.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.000000 +Radius, covalent : 1.000000 +Radius, atomic : 1.000000 +Charge state : 3 +Radius, ionic : 1.130000 +Charge state : 4 +Radius, ionic : 0.980000 +Charge state : 5 +Radius, ionic : 0.890000 + + +Atom +==== +Number : 92 +Name : Uranium +Short name : U +Color : 0.0,0.560784313725,1.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.420000 +Radius, covalent : 1.420000 +Radius, atomic : 1.000000 +Charge state : 4 +Radius, ionic : 0.970000 +Charge state : 6 +Radius, ionic : 0.800000 + + +Atom +==== +Number : 93 +Name : Neptunium +Short name : Np +Color : 0.0,0.501960784314,1.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.000000 +Radius, covalent : 1.000000 +Radius, atomic : 1.000000 +Charge state : 3 +Radius, ionic : 1.100000 +Charge state : 4 +Radius, ionic : 0.950000 +Charge state : 7 +Radius, ionic : 0.710000 + + +Atom +==== +Number : 94 +Name : Plutonium +Short name : Pu +Color : 0.0,0.419607843137,1.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.000000 +Radius, covalent : 1.000000 +Radius, atomic : 1.000000 +Charge state : 3 +Radius, ionic : 1.080000 +Charge state : 4 +Radius, ionic : 0.930000 + + +Atom +==== +Number : 95 +Name : Americium +Short name : Am +Color : 0.329411764706,0.360784313725,0.949019607843,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.000000 +Radius, covalent : 1.000000 +Radius, atomic : 1.000000 +Charge state : 3 +Radius, ionic : 1.070000 +Charge state : 4 +Radius, ionic : 0.920000 + + +Atom +==== +Number : 96 +Name : Curium +Short name : Cm +Color : 0.470588235294,0.360784313725,0.890196078431,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.000000 +Radius, covalent : 1.000000 +Radius, atomic : 1.000000 + + +Atom +==== +Number : 97 +Name : Berkelium +Short name : Bk +Color : 0.541176470588,0.309803921569,0.890196078431,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.000000 +Radius, covalent : 1.000000 +Radius, atomic : 1.000000 + + +Atom +==== +Number : 98 +Name : Californium +Short name : Cf +Color : 0.63137254902,0.211764705882,0.83137254902,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.000000 +Radius, covalent : 1.000000 +Radius, atomic : 1.000000 + + +Atom +==== +Number : 99 +Name : Einsteinium +Short name : Es +Color : 0.701960784314,0.121568627451,0.83137254902,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.000000 +Radius, covalent : 1.000000 +Radius, atomic : 1.000000 + + +Atom +==== +Number : 100 +Name : Fermium +Short name : Fm +Color : 0.701960784314,0.121568627451,0.729411764706,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.000000 +Radius, covalent : 1.000000 +Radius, atomic : 1.000000 + + +Atom +==== +Number : 101 +Name : Mendelevium +Short name : Md +Color : 0.701960784314,0.0509803921569,0.650980392157,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.000000 +Radius, covalent : 1.000000 +Radius, atomic : 1.000000 + + +Atom +==== +Number : 102 +Name : Nobelium +Short name : No +Color : 0.741176470588,0.0509803921569,0.529411764706,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.000000 +Radius, covalent : 1.000000 +Radius, atomic : 1.000000 + + +Atom +==== +Number : 103 +Name : Lawrencium +Short name : Lr +Color : 0.780392156863,0.0,0.4,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.000000 +Radius, covalent : 1.000000 +Radius, atomic : 1.000000 + + +Atom +==== +Number : 104 +Name : Vacancy +Short name : Vac +Color : 0.5,0.5,0.5,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.0 +Radius, covalent : 1.0 +Radius, atomic : 1.0 + + +Atom +==== +Number : 105 +Name : Default +Short name : Default +Color : 1.0,1.0,1.0,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.0 +Radius, covalent : 1.0 +Radius, atomic : 1.0 + + +Atom +==== +Number : 106 +Name : Stick +Short name : Stick +Color : 0.5,0.5,0.5,1.0 +Diffuse itensity : 0.8 +Specular itensity: 0.5 +Specular hard : 50 +Traceable : 1 +Shadow receive : 1 +Shadow cast : 1 +Radius used : 1.0 +Radius, covalent : 1.0 +Radius, atomic : 1.0 diff --git a/io_mesh_atomic/pdb_export.py b/io_mesh_atomic/pdb_export.py new file mode 100644 index 00000000..949ee5e2 --- /dev/null +++ b/io_mesh_atomic/pdb_export.py @@ -0,0 +1,81 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +import bpy +from io_mesh_atomic.pdb_import import ELEMENTS_DEFAULT + +class AtomPropExport(object): + __slots__ = ('element', 'location') + def __init__(self, element, location): + self.element = element + self.location = location + + +def export_pdb(obj_type, filepath_pdb): + + list_atoms = [] + for obj in bpy.context.selected_objects: + + if "STICK" in obj.name.upper(): + continue + + if obj.type not in {'MESH', 'SURFACE', 'META'}: + continue + + name = "" + for element in ELEMENTS_DEFAULT: + if element[1] in obj.name: + if element[2] == "Vac": + name = "X" + else: + name = element[2] + + if name == "": + if obj_type == "0": + name = "?" + else: + continue + + if len(obj.children) != 0: + for vertex in obj.data.vertices: + location = obj.matrix_world @ vertex.co + list_atoms.append(AtomPropExport(name, location)) + else: + if not obj.parent: + location = obj.location + list_atoms.append(AtomPropExport(name, location)) + + pdb_file_p = open(filepath_pdb, "w") + pdb_file_p.write("REMARK This pdb file has been created with Blender " + "and the addon Atomic Blender - PDB\n" + "REMARK For more details see the wiki pages of Blender.\n" + "REMARK\n" + "REMARK\n") + + for i, atom in enumerate(list_atoms): + string = "ATOM %6d%3s%24.3f%8.3f%8.3f%6.2f%6.2f%12s\n" % ( + i, atom.element, + atom.location[0], + atom.location[1], + atom.location[2], + 1.00, 1.00, atom.element) + pdb_file_p.write(string) + + pdb_file_p.close() + + return True diff --git a/io_mesh_atomic/pdb_gui.py b/io_mesh_atomic/pdb_gui.py new file mode 100644 index 00000000..76736eaa --- /dev/null +++ b/io_mesh_atomic/pdb_gui.py @@ -0,0 +1,270 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +import bpy +from bpy.types import Operator, AddonPreferences +from bpy_extras.io_utils import ImportHelper, ExportHelper +from bpy.props import ( + StringProperty, + BoolProperty, + EnumProperty, + IntProperty, + FloatProperty, + ) + +from io_mesh_atomic.pdb_import import import_pdb +from io_mesh_atomic.pdb_export import export_pdb + +# ----------------------------------------------------------------------------- +# Operators + +# This is the class for the file dialog of the importer. +class IMPORT_OT_pdb(Operator, ImportHelper): + bl_idname = "import_mesh.pdb" + bl_label = "Import Protein Data Bank(*.pdb)" + bl_options = {'PRESET', 'UNDO'} + + filename_ext = ".pdb" + filter_glob: StringProperty(default="*.pdb", options={'HIDDEN'},) + + use_center: BoolProperty( + name = "Object to origin", default=True, + description = "Put the object into the global origin") + use_camera: BoolProperty( + name="Camera", default=False, + description="Do you need a camera?") + use_light: BoolProperty( + name="Lamp", default=False, + description = "Do you need a lamp?") + ball: EnumProperty( + name="Type of ball", + description="Choose ball", + items=(('0', "NURBS", "NURBS balls"), + ('1', "Mesh" , "Mesh balls"), + ('2', "Meta" , "Metaballs")), + default='0',) + mesh_azimuth: IntProperty( + name = "Azimuth", default=32, min=1, + description = "Number of sectors (azimuth)") + mesh_zenith: IntProperty( + name = "Zenith", default=32, min=1, + description = "Number of sectors (zenith)") + scale_ballradius: FloatProperty( + name = "Balls", default=1.0, min=0.0001, + description = "Scale factor for all atom radii") + scale_distances: FloatProperty ( + name = "Distances", default=1.0, min=0.0001, + description = "Scale factor for all distances") + atomradius: EnumProperty( + name="Type", + description="Choose type of atom radius", + items=(('0', "Pre-defined", "Use pre-defined radius"), + ('1', "Atomic", "Use atomic radius"), + ('2', "van der Waals", "Use van der Waals radius")), + default='0',) + use_sticks: BoolProperty( + name="Use sticks", default=True, + description="Do you want to display the sticks?") + use_sticks_type: EnumProperty( + name="Type", + description="Choose type of stick", + items=(('0', "Dupliverts", "Use dupliverts structures"), + ('1', "Skin", "Use skin and subdivision modifier"), + ('2', "Normal", "Use simple cylinders")), + default='0',) + sticks_subdiv_view: IntProperty( + name = "SubDivV", default=2, min=1, + description="Number of subdivisions (view)") + sticks_subdiv_render: IntProperty( + name = "SubDivR", default=2, min=1, + description="Number of subdivisions (render)") + sticks_sectors: IntProperty( + name = "Sector", default=20, min=1, + description="Number of sectors of a stick") + sticks_radius: FloatProperty( + name = "Radius", default=0.2, min=0.0001, + description ="Radius of a stick") + sticks_unit_length: FloatProperty( + name = "Unit", default=0.05, min=0.0001, + description = "Length of the unit of a stick in Angstrom") + use_sticks_color: BoolProperty( + name="Color", default=True, + description="The sticks appear in the color of the atoms") + use_sticks_smooth: BoolProperty( + name="Smooth", default=True, + description="The sticks are round (sectors are not visible)") + use_sticks_bonds: BoolProperty( + name="Bonds", default=False, + description="Show double and triple bonds") + sticks_dist: FloatProperty( + name="", default = 1.1, min=1.0, max=3.0, + description="Distance between sticks measured in stick diameter") + use_sticks_one_object: BoolProperty( + name="One object", default=True, + description="All sticks are one object") + use_sticks_one_object_nr: IntProperty( + name = "No.", default=200, min=10, + description="Number of sticks to be grouped at once") + datafile: StringProperty( + name = "", description="Path to your custom data file", + maxlen = 256, default = "", subtype='FILE_PATH') + + # This thing here just guarantees that the menu entry is not active when the + # check box in the addon preferences is not activated! See __init__.py + @classmethod + def poll(cls, context): + pref = context.preferences + return pref.addons[__package__].preferences.bool_pdb + + def draw(self, context): + layout = self.layout + row = layout.row() + row.prop(self, "use_camera") + row.prop(self, "use_light") + row = layout.row() + row.prop(self, "use_center") + # Balls + box = layout.box() + row = box.row() + row.label(text="Balls / atoms") + row = box.row() + col = row.column() + col.prop(self, "ball") + row = box.row() + row.active = (self.ball == "1") + col = row.column(align=True) + col.prop(self, "mesh_azimuth") + col.prop(self, "mesh_zenith") + row = box.row() + col = row.column() + col.label(text="Scaling factors") + col = row.column(align=True) + col.prop(self, "scale_ballradius") + col.prop(self, "scale_distances") + row = box.row() + row.prop(self, "atomradius") + # Sticks + box = layout.box() + row = box.row() + row.label(text="Sticks / bonds") + row = box.row() + row.prop(self, "use_sticks") + row = box.row() + row.active = self.use_sticks + row.prop(self, "use_sticks_type") + row = box.row() + row.active = self.use_sticks + col = row.column() + if self.use_sticks_type == '0' or self.use_sticks_type == '2': + col.prop(self, "sticks_sectors") + col.prop(self, "sticks_radius") + if self.use_sticks_type == '1': + row = box.row() + row.active = self.use_sticks + row.prop(self, "sticks_subdiv_view") + row.prop(self, "sticks_subdiv_render") + row = box.row() + row.active = self.use_sticks + if self.use_sticks_type == '0': + col.prop(self, "sticks_unit_length") + col = row.column(align=True) + if self.use_sticks_type == '0': + col.prop(self, "use_sticks_color") + col.prop(self, "use_sticks_smooth") + if self.use_sticks_type == '0' or self.use_sticks_type == '2': + col.prop(self, "use_sticks_bonds") + row = box.row() + if self.use_sticks_type == '0': + row.active = self.use_sticks and self.use_sticks_bonds + row.label(text="Distance") + row.prop(self, "sticks_dist") + if self.use_sticks_type == '2': + row.active = self.use_sticks + col = row.column() + col.prop(self, "use_sticks_one_object") + col = row.column() + col.active = self.use_sticks_one_object + col.prop(self, "use_sticks_one_object_nr") + + + def execute(self, context): + # This is in order to solve this strange 'relative path' thing. + filepath_pdb = bpy.path.abspath(self.filepath) + + # Execute main routine + import_pdb(self.ball, + self.mesh_azimuth, + self.mesh_zenith, + self.scale_ballradius, + self.atomradius, + self.scale_distances, + self.use_sticks, + self.use_sticks_type, + self.sticks_subdiv_view, + self.sticks_subdiv_render, + self.use_sticks_color, + self.use_sticks_smooth, + self.use_sticks_bonds, + self.use_sticks_one_object, + self.use_sticks_one_object_nr, + self.sticks_unit_length, + self.sticks_dist, + self.sticks_sectors, + self.sticks_radius, + self.use_center, + self.use_camera, + self.use_light, + filepath_pdb) + + return {'FINISHED'} + + +# This is the class for the file dialog of the exporter. +class EXPORT_OT_pdb(Operator, ExportHelper): + bl_idname = "export_mesh.pdb" + bl_label = "Export Protein Data Bank(*.pdb)" + filename_ext = ".pdb" + + filter_glob: StringProperty( + default="*.pdb", options={'HIDDEN'},) + + atom_pdb_export_type: EnumProperty( + name="Type of Objects", + description="Choose type of objects", + items=(('0', "All", "Export all active objects"), + ('1', "Elements", "Export only those active objects which have" + " a proper element name")), + default='1',) + + # This thing here just guarantees that the menu entry is not active when the + # check box in the addon preferences is not activated! See __init__.py + @classmethod + def poll(cls, context): + pref = context.preferences + return pref.addons[__package__].preferences.bool_pdb + + def draw(self, context): + layout = self.layout + row = layout.row() + row.prop(self, "atom_pdb_export_type") + + def execute(self, context): + export_pdb(self.atom_pdb_export_type, + bpy.path.abspath(self.filepath)) + + return {'FINISHED'} diff --git a/io_mesh_atomic/pdb_import.py b/io_mesh_atomic/pdb_import.py new file mode 100644 index 00000000..06a808c0 --- /dev/null +++ b/io_mesh_atomic/pdb_import.py @@ -0,0 +1,1549 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +import os +import bpy +import bmesh +from math import pi, cos, sin, sqrt, ceil +from mathutils import Vector, Matrix +from copy import copy + +# ----------------------------------------------------------------------------- +# Atom, stick 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), +) + +# This list here contains all data of the elements and will be used during +# runtime. It is a list of classes. +# During executing Atomic Blender, the list will be initialized with the fixed +# data from above via the class structure below (ElementProp). We +# have then one fixed list (above), which will never be changed, and a list of +# classes with same data. The latter can be modified via 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') + def __init__(self, number, name, short_name, color, radii, radii_ionic): + self.number = number + self.name = name + self.short_name = short_name + self.color = color + self.radii = radii + self.radii_ionic = radii_ionic + +# This is the class, which stores the properties of one atom. +class AtomProp(object): + __slots__ = ('element', 'name', 'location', 'radius', 'color', 'material') + def __init__(self, element, name, location, radius, color, material): + self.element = element + self.name = name + self.location = location + self.radius = radius + self.color = color + self.material = material + +# This is the class, which stores the two atoms of one stick. +class StickProp(object): + __slots__ = ('atom1', 'atom2', 'number', 'dist') + def __init__(self, atom1, atom2, number, dist): + self.atom1 = atom1 + self.atom2 = atom2 + self.number = number + self.dist = dist + +# ----------------------------------------------------------------------------- +# Some basic routines + + +# The function, which reads all necessary properties of the 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 = [] + + li = ElementProp(item[0],item[1],item[2],item[3], + radii,radii_ionic) + ELEMENTS.append(li) + + +# The function, which reads the x,y,z positions of all atoms in a PDB +# file. +# +# filepath_pdb: path to pdb file +# radiustype : '0' default +# '1' atomic radii +# '2' van der Waals +def read_pdb_file(filepath_pdb, radiustype): + + # The list of all atoms as read from the PDB file. + all_atoms = [] + + # Open the pdb file ... + filepath_pdb_p = open(filepath_pdb, "r") + + #Go to the line, in which "ATOM" or "HETATM" appears. + for line in filepath_pdb_p: + split_list = line.split(' ') + if "ATOM" in split_list[0]: + break + if "HETATM" in split_list[0]: + break + + j = 0 + # This is in fact an endless 'while loop', ... + while j > -1: + + # ... the loop is broken here (EOF) ... + if line == "": + break + + # If there is a "TER" we need to put empty entries into the lists + # in order to not destroy the order of atom numbers and same numbers + # used for sticks. "TER? What is that?" TER indicates the end of a + # list of ATOM/HETATM records for a chain. + if "TER" in line: + short_name = "TER" + name = "TER" + radius = 0.0 + # 2019-03-14, New + color = [0,0,0, 0] + location = Vector((0,0,0)) + # Append the TER into the list. Material remains empty so far. + all_atoms.append(AtomProp(short_name, + name, + location, + radius, + color,[])) + + # If 'ATOM or 'HETATM' appears in the line then do ... + elif "ATOM" in line or "HETATM" in line: + + # What follows is due to deviations which appear from PDB to + # PDB file. It is very special! + # + # PLEASE, DO NOT CHANGE! ............................... from here + if line[12:13] == " " or line[12:13].isdigit() == True: + short_name = line[13:14] + if line[14:15].islower() == True: + short_name = short_name + line[14:15] + elif line[12:13].isupper() == True: + short_name = line[12:13] + if line[13:14].isalpha() == True: + short_name = short_name + line[13:14] + else: + print("Atomic Blender: Strange error in PDB file.\n" + "Look for element names at positions 13-16 and 78-79.\n") + return -1 + + if len(line) >= 78: + + if line[76:77] == " ": + short_name2 = line[76:77] + else: + short_name2 = line[76:78] + + if short_name2.isalpha() == True: + FOUND = False + for element in ELEMENTS: + if str.upper(short_name2) == str.upper(element.short_name): + FOUND = True + break + if FOUND == False: + short_name = short_name2 + + # ....................................................... to here. + + # Go through all elements and find the element of the current atom. + FLAG_FOUND = False + for element in ELEMENTS: + if str.upper(short_name) == str.upper(element.short_name): + # Give the atom its proper names, color and radius: + short_name = str.upper(element.short_name) + name = element.name + # int(radiustype) => type of radius: + # pre-defined (0), atomic (1) or van der Waals (2) + radius = float(element.radii[int(radiustype)]) + color = element.color + FLAG_FOUND = True + break + + # Is it a vacancy or an 'unknown atom' ? + if FLAG_FOUND == False: + # Give this atom also a name. If it is an 'X' then it is a + # vacancy. Otherwise ... + if "X" in short_name: + short_name = "VAC" + name = "Vacancy" + radius = float(ELEMENTS[-3].radii[int(radiustype)]) + color = ELEMENTS[-3].color + # ... take what is written in the PDB file. These are somewhat + # unknown atoms. This should never happen, the element list is + # almost complete. However, we do this due to security reasons. + else: + short_name = str.upper(short_name) + name = str.upper(short_name) + radius = float(ELEMENTS[-2].radii[int(radiustype)]) + color = ELEMENTS[-2].color + + # x,y and z are at fixed positions in the PDB file. + x = float(line[30:38].rsplit()[0]) + y = float(line[38:46].rsplit()[0]) + z = float(line[46:55].rsplit()[0]) + + location = Vector((x,y,z)) + + j += 1 + + # Append the atom to the list. Material remains empty so far. + all_atoms.append(AtomProp(short_name, + name, + location, + radius, + color,[])) + + line = filepath_pdb_p.readline() + line = line[:-1] + + filepath_pdb_p.close() + # From above it can be clearly seen that j is now the number of all atoms. + Number_of_total_atoms = j + + return (Number_of_total_atoms, all_atoms) + + +# The function, which reads the sticks in a PDB file. +def read_pdb_file_sticks(filepath_pdb, use_sticks_bonds, all_atoms): + + # The list of all sticks. + all_sticks = [] + + # Open the PDB file. + filepath_pdb_p = open(filepath_pdb, "r") + + line = filepath_pdb_p.readline() + split_list = line.split(' ') + + # Go to the first entry + if "CONECT" not in split_list[0]: + for line in filepath_pdb_p: + split_list = line.split(' ') + if "CONECT" in split_list[0]: + break + + Number_of_sticks = 0 + sticks_double = 0 + j = 0 + # This is in fact an endless while loop, ... + while j > -1: + + # ... which is broken here (EOF) ... + if line == "": + break + # ... or here, when no 'CONECT' appears anymore. + if "CONECT" not in line: + break + + # Note 2019-03-16: in a PDB file the identifier for sticks is called + # 'CONECT' and NOT 'CONNECT'! Please leave this as is, otherwise the + # sticks are NOT correctly loaded. + + # The strings of the atom numbers do have a clear position in the file + # (From 7 to 12, from 13 to 18 and so on.) and one needs to consider + # this. One could also use the split function but then one gets into + # trouble if there are lots of atoms: For instance, it may happen that + # one has + # CONECT 11111 22244444 + # + # In Fact it means that atom No. 11111 has a connection with atom + # No. 222 but also with atom No. 44444. The split function would give + # me only two numbers (11111 and 22244444), which is wrong. + + # Cut spaces from the right and 'CONECT' at the beginning + line = line.rstrip() + line = line[6:] + # Amount of loops + length = len(line) + loops = int(length/5) + + # List of atoms + atom_list = [] + for i in range(loops): + number = line[5*i:5*(i+1)].rsplit() + if number != []: + if number[0].isdigit() == True: + atom_number = int(number[0]) + atom_list.append(atom_number) + + # The first atom is connected with all the others in the list. + atom1 = atom_list[0] + + # For all the other atoms in the list do: + for atom2 in atom_list[1:]: + + if use_sticks_bonds == True: + number = atom_list[1:].count(atom2) + + if number == 2 or number == 3: + basis_list = list(set(atom_list[1:])) + + if len(basis_list) > 1: + basis1 = (all_atoms[atom1-1].location + - all_atoms[basis_list[0]-1].location) + basis2 = (all_atoms[atom1-1].location + - all_atoms[basis_list[1]-1].location) + plane_n = basis1.cross(basis2) + + dist_n = (all_atoms[atom1-1].location + - all_atoms[atom2-1].location) + dist_n = dist_n.cross(plane_n) + dist_n = dist_n / dist_n.length + else: + dist_n = (all_atoms[atom1-1].location + - all_atoms[atom2-1].location) + dist_n = Vector((dist_n[1],-dist_n[0],0)) + dist_n = dist_n / dist_n.length + + elif number > 3: + number = 1 + dist_n = None + else: + dist_n = None + else: + number = 1 + dist_n = None + + # Note that in a PDB file, sticks of one atom pair can appear a + # couple of times. (Only god knows why ...) + # So, does a stick between the considered atoms already exist? + FLAG_BAR = False + for k in range(Number_of_sticks): + if ((all_sticks[k].atom1 == atom1 and all_sticks[k].atom2 == atom2) or + (all_sticks[k].atom2 == atom1 and all_sticks[k].atom1 == atom2)): + sticks_double += 1 + # If yes, then FLAG on 'True'. + FLAG_BAR = True + break + + # If the stick is not yet registered (FLAG_BAR == False), then + # register it! + if FLAG_BAR == False: + all_sticks.append(StickProp(atom1,atom2,number,dist_n)) + Number_of_sticks += 1 + j += 1 + + line = filepath_pdb_p.readline() + line = line.rstrip() + + filepath_pdb_p.close() + + return all_sticks + + +# Function, which produces a cylinder. All is somewhat easy to understand. +def build_stick(radius, length, sectors, element_name): + + dphi = 2.0 * pi/(float(sectors)-1) + + # Vertices + vertices_top = [Vector((0,0,length / 2.0))] + vertices_bottom = [Vector((0,0,-length / 2.0))] + vertices = [] + for i in range(sectors-1): + x = radius * cos( dphi * i ) + y = radius * sin( dphi * i ) + z = length / 2.0 + vertex = Vector((x,y,z)) + vertices_top.append(vertex) + z = -length / 2.0 + vertex = Vector((x,y,z)) + vertices_bottom.append(vertex) + vertices = vertices_top + vertices_bottom + + # Side facets (Cylinder) + faces1 = [] + for i in range(sectors-1): + if i == sectors-2: + faces1.append( [i+1, 1, 1+sectors, i+1+sectors] ) + else: + faces1.append( [i+1, i+2, i+2+sectors, i+1+sectors] ) + + # Top facets + faces2 = [] + for i in range(sectors-1): + if i == sectors-2: + face_top = [0,sectors-1,1] + face_bottom = [sectors,2*sectors-1,sectors+1] + else: + face_top = [0] + face_bottom = [sectors] + for j in range(2): + face_top.append(i+j+1) + face_bottom.append(i+j+1+sectors) + faces2.append(face_top) + faces2.append(face_bottom) + + # Build the mesh, Cylinder + cylinder = bpy.data.meshes.new(element_name+"_sticks_cylinder") + cylinder.from_pydata(vertices, [], faces1) + cylinder.update() + new_cylinder = bpy.data.objects.new(element_name+"_sticks_cylinder", cylinder) + # Attention: the linking will be done a few moments later, after this + # is done definition. + + # Build the mesh, Cups + cups = bpy.data.meshes.new(element_name+"_sticks_cup") + cups.from_pydata(vertices, [], faces2) + cups.update() + new_cups = bpy.data.objects.new(element_name+"_sticks_cup", cups) + # Attention: the linking will be done a few moments later, after this + # is done definition. + + return (new_cylinder, new_cups) + + +# Rotate an object. +def rotate_object(rot_mat, obj): + + bpy.ops.object.select_all(action='DESELECT') + obj.select_set(True) + + # Decompose world_matrix's components, and from them assemble 4x4 matrices. + orig_loc, orig_rot, orig_scale = obj.matrix_world.decompose() + + orig_loc_mat = Matrix.Translation(orig_loc) + orig_rot_mat = orig_rot.to_matrix().to_4x4() + orig_scale_mat = (Matrix.Scale(orig_scale[0],4,(1,0,0)) @ + Matrix.Scale(orig_scale[1],4,(0,1,0)) @ + Matrix.Scale(orig_scale[2],4,(0,0,1))) + + # Assemble the new matrix. + obj.matrix_world = orig_loc_mat @ rot_mat @ orig_rot_mat @ orig_scale_mat + + +# Function, which puts a camera and light source into the 3D scene +def camera_light_source(use_camera, + use_light, + object_center_vec, + object_size): + + camera_factor = 15.0 + + # If chosen a camera is put into the scene. + if use_camera == True: + + # Assume that the object is put into the global origin. Then, the + # camera is moved in x and z direction, not in y. The object has its + # size at distance sqrt(object_size) from the origin. So, move the + # camera by this distance times a factor of camera_factor in x and z. + # Then add x, y and z of the origin of the object. + object_camera_vec = Vector((sqrt(object_size) * camera_factor, + 0.0, + sqrt(object_size) * camera_factor)) + camera_xyz_vec = object_center_vec + object_camera_vec + + # Create the camera + camera_data = bpy.data.cameras.new("A_camera") + camera_data.lens = 45 + camera_data.clip_end = 500.0 + camera = bpy.data.objects.new("A_camera", camera_data) + camera.location = camera_xyz_vec + bpy.context.collection.objects.link(camera) + + # Here the camera is rotated such it looks towards the center of + # the object. The [0.0, 0.0, 1.0] vector along the z axis + z_axis_vec = Vector((0.0, 0.0, 1.0)) + # The angle between the last two vectors + angle = object_camera_vec.angle(z_axis_vec, 0) + # The cross-product of z_axis_vec and object_camera_vec + axis_vec = z_axis_vec.cross(object_camera_vec) + # Rotate 'axis_vec' by 'angle' and convert this to euler parameters. + # 4 is the size of the matrix. + camera.rotation_euler = Matrix.Rotation(angle, 4, axis_vec).to_euler() + + # Rotate the camera around its axis by 90° such that we have a nice + # camera position and view onto the object. + bpy.ops.object.select_all(action='DESELECT') + camera.select_set(True) + + # Rotate the camera around its axis 'object_camera_vec' by 90° such + # that we have a nice camera view onto the object. + matrix_rotation = Matrix.Rotation(90/360*2*pi, 4, object_camera_vec) + rotate_object(matrix_rotation, camera) + + # Here a lamp is put into the scene, if chosen. + if use_light == True: + + # This is the distance from the object measured in terms of % + # of the camera distance. It is set onto 50% (1/2) distance. + light_dl = sqrt(object_size) * 15 * 0.5 + # This is a factor to which extend the lamp shall go to the right + # (from the camera point of view). + light_dy_right = light_dl * (3.0/4.0) + + # Create x, y and z for the lamp. + object_light_vec = Vector((light_dl,light_dy_right,light_dl)) + light_xyz_vec = object_center_vec + object_light_vec + + # Create the lamp + light_data = bpy.data.lights.new(name="A_light", type="SUN") + light_data.distance = 500.0 + light_data.energy = 3.0 + lamp = bpy.data.objects.new("A_light", light_data) + lamp.location = light_xyz_vec + bpy.context.collection.objects.link(lamp) + + # Some settings for the World: a bit ambient occlusion + bpy.context.scene.world.light_settings.use_ambient_occlusion = True + bpy.context.scene.world.light_settings.ao_factor = 0.1 + # Some properties for cycles + lamp.data.use_nodes = True + lmp_P_BSDF = lamp.data.node_tree.nodes['Emission'] + lmp_P_BSDF.inputs['Strength'].default_value = 5 + + +# Function, which draws the atoms of one type (balls). This is one +# dupliverts structure then. +# Return: the dupliverts structure +def draw_atoms_one_type(draw_all_atoms_type, + Ball_type, + Ball_azimuth, + Ball_zenith, + Ball_radius_factor, + object_center_vec, + collection_molecule): + + # Create the vertices composed of the coordinates of all atoms of one type + atom_vertices = [] + for atom in draw_all_atoms_type: + # In fact, the object is created in the World's origin. + # This is why 'object_center_vec' is subtracted. At the end + # the whole object is translated back to 'object_center_vec'. + atom_vertices.append(atom[2] - object_center_vec) + + # IMPORTANT: First, we create a collection of the element, which contains + # the atoms (balls + mesh) AND the sticks! The definition dealing with the + # sticks will put the sticks inside this collection later on. + coll_element_name = atom[0] # the element name + # Create the new collection and ... + coll_element = bpy.data.collections.new(coll_element_name) + # ... link it to the collection, which contains all parts of the + # molecule. + collection_molecule.children.link(coll_element) + + # Now, create a collection for the atoms, which includes the representative + # ball and the mesh. + coll_atom_name = atom[0] + "_atom" + # Create the new collection and ... + coll_atom = bpy.data.collections.new(coll_atom_name) + # ... link it to the collection, which contains all parts of the + # element (ball and mesh). + coll_element.children.link(coll_atom) + + # Build the mesh + atom_mesh = bpy.data.meshes.new("Mesh_"+atom[0]) + atom_mesh.from_pydata(atom_vertices, [], []) + atom_mesh.update() + new_atom_mesh = bpy.data.objects.new(atom[0] + "_mesh", atom_mesh) + + # Link active object to the new collection + coll_atom.objects.link(new_atom_mesh) + + # Now, build a representative sphere (atom). + if atom[0] == "Vacancy": + bpy.ops.mesh.primitive_cube_add( + view_align=False, enter_editmode=False, + location=(0.0, 0.0, 0.0), + rotation=(0.0, 0.0, 0.0)) + else: + # NURBS balls + if Ball_type == "0": + bpy.ops.surface.primitive_nurbs_surface_sphere_add( + view_align=False, enter_editmode=False, + location=(0,0,0), rotation=(0.0, 0.0, 0.0)) + # UV balls + elif Ball_type == "1": + bpy.ops.mesh.primitive_uv_sphere_add( + segments=Ball_azimuth, ring_count=Ball_zenith, + size=1, view_align=False, enter_editmode=False, + location=(0,0,0), rotation=(0, 0, 0)) + # Meta balls + elif Ball_type == "2": + bpy.ops.object.metaball_add(type='BALL', view_align=False, + enter_editmode=False, location=(0, 0, 0), + rotation=(0, 0, 0)) + + ball = bpy.context.view_layer.objects.active + # Hide this ball because its appearance has no meaning. It is just the + # representative ball. The ball is visible at the vertices of the mesh. + # Rememmber, this is a dupliverts construct! + ball.hide_set(True) + # Scale up/down the ball radius. + ball.scale = (atom[3]*Ball_radius_factor,) * 3 + + if atom[0] == "Vacancy": + ball.name = atom[0] + "_cube" + else: + ball.name = atom[0] + "_ball" + + ball.active_material = atom[1] + ball.parent = new_atom_mesh + new_atom_mesh.instance_type = 'VERTS' + # The object is back translated to 'object_center_vec'. + new_atom_mesh.location = object_center_vec + + # Note the collection where the ball was placed into. + coll_all = ball.users_collection + if len(coll_all) > 0: + coll_past = coll_all[0] + else: + coll_past = bpy.context.scene.collection + + # Put the atom into the new collection 'atom' and ... + coll_atom.objects.link(ball) + # ... unlink the atom from the other collection. + coll_past.objects.unlink(ball) + + return new_atom_mesh, coll_element + + +# Function, which draws the sticks with help of the dupliverts technique. +# Return: list of dupliverts structures. +def draw_sticks_dupliverts(all_atoms, + atom_all_types_list, + center, + all_sticks, + Stick_diameter, + Stick_sectors, + Stick_unit, + Stick_dist, + use_sticks_smooth, + use_sticks_color, + list_coll_elements): + + dl = Stick_unit + + if use_sticks_color == False: + stick_material = bpy.data.materials.new(ELEMENTS[-1].name) + stick_material.diffuse_color = ELEMENTS[-1].color + + # Sort the sticks and put them into a new list such that ... + sticks_all_lists = [] + if use_sticks_color == True: + for atom_type in atom_all_types_list: + if atom_type[0] == "TER": + continue + sticks_list = [] + for stick in all_sticks: + for repeat in range(stick.number): + + atom1 = copy(all_atoms[stick.atom1-1].location)-center + atom2 = copy(all_atoms[stick.atom2-1].location)-center + + dist = Stick_diameter * Stick_dist + + if stick.number == 2: + if repeat == 0: + atom1 += (stick.dist * dist) + atom2 += (stick.dist * dist) + if repeat == 1: + atom1 -= (stick.dist * dist) + atom2 -= (stick.dist * dist) + + if stick.number == 3: + if repeat == 0: + atom1 += (stick.dist * dist) + atom2 += (stick.dist * dist) + if repeat == 2: + atom1 -= (stick.dist * dist) + atom2 -= (stick.dist * dist) + + dv = atom1 - atom2 + n = dv / dv.length + if atom_type[0] == all_atoms[stick.atom1-1].name: + location = atom1 + name = "_" + all_atoms[stick.atom1-1].name + material = all_atoms[stick.atom1-1].material + sticks_list.append([name, location, dv, material]) + if atom_type[0] == all_atoms[stick.atom2-1].name: + location = atom1 - n * dl * int(ceil(dv.length / (2.0 * dl))) + name = "_" + all_atoms[stick.atom2-1].name + material = all_atoms[stick.atom2-1].material + sticks_list.append([name, location, dv, material]) + + if sticks_list != []: + sticks_all_lists.append(sticks_list) + else: + sticks_list = [] + for stick in all_sticks: + + if stick.number > 3: + stick.number = 1 + + for repeat in range(stick.number): + + atom1 = copy(all_atoms[stick.atom1-1].location)-center + atom2 = copy(all_atoms[stick.atom2-1].location)-center + + dist = Stick_diameter * Stick_dist + + if stick.number == 2: + if repeat == 0: + atom1 += (stick.dist * dist) + atom2 += (stick.dist * dist) + if repeat == 1: + atom1 -= (stick.dist * dist) + atom2 -= (stick.dist * dist) + if stick.number == 3: + if repeat == 0: + atom1 += (stick.dist * dist) + atom2 += (stick.dist * dist) + if repeat == 2: + atom1 -= (stick.dist * dist) + atom2 -= (stick.dist * dist) + + dv = atom1 - atom2 + n = dv / dv.length + location = atom1 + material = stick_material + sticks_list.append(["", location, dv, material]) + + sticks_all_lists.append(sticks_list) + + atom_object_list = [] + # ... the sticks in the list can be drawn: + for stick_list in sticks_all_lists: + vertices = [] + faces = [] + i = 0 + + # What follows is school mathematics! :-) + for stick in stick_list: + + dv = stick[2] + v1 = stick[1] + n = dv / dv.length + gamma = -n.dot(v1) + b = v1 + gamma * n + n_b = b / b.length + + if use_sticks_color == True: + loops = int(ceil(dv.length / (2.0 * dl))) + else: + loops = int(ceil(dv.length / dl)) + + for j in range(loops): + + g = v1 - n * dl / 2.0 - n * dl * j + p1 = g + n_b * Stick_diameter + p2 = g - n_b * Stick_diameter + p3 = g - n_b.cross(n) * Stick_diameter + p4 = g + n_b.cross(n) * Stick_diameter + + vertices.append(p1) + vertices.append(p2) + vertices.append(p3) + vertices.append(p4) + faces.append((i*4+0,i*4+2,i*4+1,i*4+3)) + i += 1 + + # Create a collection for the sticks, which includes the representative + # cylinders, cups and the mesh. + coll_name = stick[0][1:] + "_sticks" + # Create the collection and ... + coll = bpy.data.collections.new(coll_name) + # ... link it to the collection, which contains all parts of the + # element. 'stick[0][1:]' contains the name of the element! + for coll_element_from_list in list_coll_elements: + if stick[0][1:] in coll_element_from_list.name: + break + coll_element_from_list.children.link(coll) + + # Build the mesh. + mesh = bpy.data.meshes.new("Sticks_"+stick[0][1:]) + mesh.from_pydata(vertices, [], faces) + mesh.update() + new_mesh = bpy.data.objects.new(stick[0][1:]+"_sticks_mesh", mesh) + # Link active object to the new collection + coll.objects.link(new_mesh) + + # Build the object. + # Get the cylinder from the 'build_stick' function. + object_stick = build_stick(Stick_diameter, + dl, + Stick_sectors, + stick[0][1:]) + # Link active object to the new collection + coll.objects.link(object_stick[0]) + coll.objects.link(object_stick[1]) + + # Hide these objects because their appearance has no meaning. They are + # just the representative objects. The cylinder and cups are visible at + # the vertices of the mesh. Rememmber, this is a dupliverts construct! + object_stick[0].hide_set(True) + object_stick[1].hide_set(True) + + stick_cylinder = object_stick[0] + stick_cylinder.active_material = stick[3] + stick_cups = object_stick[1] + stick_cups.active_material = stick[3] + + # Smooth the cylinders. + if use_sticks_smooth == True: + bpy.ops.object.select_all(action='DESELECT') + stick_cylinder.select_set(True) + stick_cups.select_set(True) + bpy.ops.object.shade_smooth() + + # Parenting the mesh to the cylinder. + stick_cylinder.parent = new_mesh + stick_cups.parent = new_mesh + new_mesh.instance_type = 'FACES' + new_mesh.location = center + atom_object_list.append(new_mesh) + + # Return the list of dupliverts structures. + return atom_object_list + + +# Function, which draws the sticks with help of the skin and subdivision +# modifiers. +def draw_sticks_skin(all_atoms, + all_sticks, + Stick_diameter, + use_sticks_smooth, + sticks_subdiv_view, + sticks_subdiv_render, + coll_molecule): + + # These counters are for the edges, in the shape [i,i+1]. + i = 0 + + # This is the list of vertices, containing the atom position + # (vectors)). + stick_vertices = [] + # This is the 'same' list, which contains not vector position of + # the atoms but their numbers. It is used to handle the edges. + stick_vertices_nr = [] + # This is the list of edges. + stick_edges = [] + + # Go through the list of all sticks. For each stick do: + for stick in all_sticks: + + # Each stick has two atoms = two vertices. + + """ + [ 0,1 , 3,4 , 0,8 , 7,3] + [[0,1], [2,3], [4,5], [6,7]] + + [ 0,1 , 3,4 , x,8 , 7,x] x:deleted + [[0,1], [2,3], [0,5], [6,2]] + """ + + # Check, if the vertex (atom) is already in the vertex list. + # edge: [s1,s2] + FLAG_s1 = False + s1 = 0 + for stick2 in stick_vertices_nr: + if stick2 == stick.atom1-1: + FLAG_s1 = True + break + s1 += 1 + FLAG_s2 = False + s2 = 0 + for stick2 in stick_vertices_nr: + if stick2 == stick.atom2-1: + FLAG_s2 = True + break + s2 += 1 + + # If the vertex (atom) is not yet in the vertex list: + # append the number of atom and the vertex to the two lists. + # For the first atom: + if FLAG_s1 == False: + atom1 = copy(all_atoms[stick.atom1-1].location) + stick_vertices.append(atom1) + stick_vertices_nr.append(stick.atom1-1) + # For the second atom: + if FLAG_s2 == False: + atom2 = copy(all_atoms[stick.atom2-1].location) + stick_vertices.append(atom2) + stick_vertices_nr.append(stick.atom2-1) + + # Build the edges: + + # If both vertices (atoms) were not in the lists, then + # the edge is simply [i,i+1]. These are two new vertices + # (atoms), so increase i by 2. + if FLAG_s1 == False and FLAG_s2 == False: + stick_edges.append([i,i+1]) + i += 2 + # Both vertices (atoms) were already in the list, so then + # use the vertices (atoms), which already exist. They are + # at positions s1 and s2. + if FLAG_s1 == True and FLAG_s2 == True: + stick_edges.append([s1,s2]) + # The following two if cases describe the situation that + # only one vertex (atom) was in the list. Since only ONE + # new vertex was added, increase i by one. + if FLAG_s1 == True and FLAG_s2 == False: + stick_edges.append([s1,i]) + i += 1 + if FLAG_s1 == False and FLAG_s2 == True: + stick_edges.append([i,s2]) + i += 1 + + # Build the mesh of the sticks + stick_mesh = bpy.data.meshes.new("Mesh_sticks") + stick_mesh.from_pydata(stick_vertices, stick_edges, []) + stick_mesh.update() + new_stick_mesh = bpy.data.objects.new("Sticks", stick_mesh) + # Link the active mesh to the molecule collection + coll_molecule.objects.link(new_stick_mesh) + + # Apply the skin modifier. + new_stick_mesh.modifiers.new(name="Sticks_skin", type='SKIN') + # Smooth the skin surface if this option has been chosen. + new_stick_mesh.modifiers[0].use_smooth_shade = use_sticks_smooth + # Apply the Subdivision modifier. + new_stick_mesh.modifiers.new(name="Sticks_subsurf", type='SUBSURF') + # Options: choose the levels + new_stick_mesh.modifiers[1].levels = sticks_subdiv_view + new_stick_mesh.modifiers[1].render_levels = sticks_subdiv_render + + stick_material = bpy.data.materials.new(ELEMENTS[-1].name) + stick_material.diffuse_color = ELEMENTS[-1].color + new_stick_mesh.active_material = stick_material + + # This is for putting the radius of the sticks onto + # the desired value 'Stick_diameter' + bpy.context.view_layer.objects.active = new_stick_mesh + # EDIT mode + bpy.ops.object.mode_set(mode='EDIT', toggle=False) + bm = bmesh.from_edit_mesh(new_stick_mesh.data) + bpy.ops.mesh.select_all(action='DESELECT') + + # Select all vertices + for v in bm.verts: + v.select = True + + # This is somewhat a factor for the radius. + r_f = 4.0 + # Apply operator 'skin_resize'. + bpy.ops.transform.skin_resize(value=(Stick_diameter*r_f, + Stick_diameter*r_f, + Stick_diameter*r_f), + constraint_axis=(False, False, False), + orient_type='GLOBAL', + mirror=False, + proportional='DISABLED', + proportional_edit_falloff='SMOOTH', + proportional_size=1, + snap=False, + snap_target='CLOSEST', + snap_point=(0, 0, 0), + snap_align=False, + snap_normal=(0, 0, 0), + release_confirm=False) + # Back to the OBJECT mode. + bpy.ops.object.mode_set(mode='OBJECT', toggle=False) + + return new_stick_mesh + + +# Draw the sticks the normal way: connect the atoms by simple cylinders. +# Two options: 1. single cylinders parented to an empty +# 2. one single mesh object +def draw_sticks_normal(all_atoms, + all_sticks, + center, + Stick_diameter, + Stick_sectors, + use_sticks_smooth, + use_sticks_one_object, + use_sticks_one_object_nr, + coll_molecule): + + stick_material = bpy.data.materials.new(ELEMENTS[-1].name) + stick_material.diffuse_color = ELEMENTS[-1].color + + up_axis = Vector([0.0, 0.0, 1.0]) + + # For all sticks, do ... + list_group = [] + list_group_sub = [] + counter = 0 + for stick in all_sticks: + + # The vectors of the two atoms + atom1 = all_atoms[stick.atom1-1].location-center + atom2 = all_atoms[stick.atom2-1].location-center + # Location + location = (atom1 + atom2) * 0.5 + # The difference of both vectors + v = (atom2 - atom1) + # Angle with respect to the z-axis + angle = v.angle(up_axis, 0) + # Cross-product between v and the z-axis vector. It is the + # vector of rotation. + axis = up_axis.cross(v) + # Calculate Euler angles + euler = Matrix.Rotation(angle, 4, axis).to_euler() + # Create stick + stick = bpy.ops.mesh.primitive_cylinder_add(vertices=Stick_sectors, + radius=Stick_diameter, + depth=v.length, + end_fill_type='NGON', + view_align=False, + enter_editmode=False, + location=location, + rotation=(0, 0, 0)) + # Put the stick into the scene ... + stick = bpy.context.view_layer.objects.active + # ... and rotate the stick. + stick.rotation_euler = euler + # ... and name + stick.name = "Stick_Cylinder" + counter += 1 + + # Smooth the cylinder. + if use_sticks_smooth == True: + bpy.ops.object.select_all(action='DESELECT') + stick.select_set(True) + bpy.ops.object.shade_smooth() + + list_group_sub.append(stick) + + if use_sticks_one_object == True: + if counter == use_sticks_one_object_nr: + bpy.ops.object.select_all(action='DESELECT') + for stick in list_group_sub: + stick.select_set(True) + bpy.ops.object.join() + list_group.append(bpy.context.view_layer.objects.active) + bpy.ops.object.select_all(action='DESELECT') + list_group_sub = [] + counter = 0 + else: + # Material ... + stick.active_material = stick_material + + if use_sticks_one_object == True: + bpy.ops.object.select_all(action='DESELECT') + for stick in list_group_sub: + stick.select_set(True) + bpy.ops.object.join() + list_group.append(bpy.context.view_layer.objects.active) + bpy.ops.object.select_all(action='DESELECT') + + for group in list_group: + group.select_set(True) + bpy.ops.object.join() + bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', + center='MEDIAN') + sticks = bpy.context.view_layer.objects.active + sticks.active_material = stick_material + + sticks.location += center + + # Collections + # =========== + # Note the collection where the sticks were placed into. + coll_all = sticks.users_collection + if len(coll_all) > 0: + coll_past = coll_all[0] + else: + coll_past = bpy.context.scene.collection + + # Link the sticks with the collection of the molecule ... + coll_molecule.objects.link(sticks) + # ... and unlink them from the collection it has been before. + coll_past.objects.unlink(sticks) + + return sticks + else: + # Here we use an empty ... + bpy.ops.object.empty_add(type='ARROWS', + view_align=False, + location=(0, 0, 0), + rotation=(0, 0, 0)) + sticks_empty = bpy.context.view_layer.objects.active + sticks_empty.name = "A_sticks_empty" + # ... that is parent to all sticks. With this, we can better move + # all sticks if necessary. + for stick in list_group_sub: + stick.parent = sticks_empty + + sticks_empty.location += center + + # Collections + # =========== + # Create a collection that will contain all sticks + the empty and ... + coll = bpy.data.collections.new("Sticks") + # ... link it to the collection, which contains all parts of the + # molecule. + coll_molecule.children.link(coll) + # Now, create a collection that only contains the sticks and ... + coll_cylinder = bpy.data.collections.new("Sticks_cylinders") + # ... link it to the collection, which contains the sticks and empty. + coll.children.link(coll_cylinder) + + # Note the collection where the empty was placed into, ... + coll_all = sticks_empty.users_collection + if len(coll_all) > 0: + coll_past = coll_all[0] + else: + coll_past = bpy.context.scene.collection + # ... link the empty with the new collection ... + coll.objects.link(sticks_empty) + # ... and unlink it from the old collection where it has been before. + coll_past.objects.unlink(sticks_empty) + + # Note the collection where the cylinders were placed into, ... + coll_all = list_group_sub[0].users_collection + if len(coll_all) > 0: + coll_past = coll_all[0] + else: + coll_past = bpy.context.scene.collection + + for stick in list_group_sub: + # ... link each stick with the new collection ... + coll_cylinder.objects.link(stick) + # ... and unlink it from the old collection. + coll_past.objects.unlink(stick) + + return sticks_empty + + +# ----------------------------------------------------------------------------- +# The main routine + +def import_pdb(Ball_type, + Ball_azimuth, + Ball_zenith, + Ball_radius_factor, + radiustype, + Ball_distance_factor, + use_sticks, + use_sticks_type, + sticks_subdiv_view, + sticks_subdiv_render, + use_sticks_color, + use_sticks_smooth, + use_sticks_bonds, + use_sticks_one_object, + use_sticks_one_object_nr, + Stick_unit, Stick_dist, + Stick_sectors, + Stick_diameter, + put_to_center, + use_camera, + use_light, + filepath_pdb): + + # List of materials + atom_material_list = [] + + # A list of ALL objects which are loaded (needed for selecting the loaded + # structure. + atom_object_list = [] + + # ------------------------------------------------------------------------ + # INITIALIZE THE ELEMENT LIST + + read_elements() + + # ------------------------------------------------------------------------ + # READING DATA OF ATOMS + + (Number_of_total_atoms, all_atoms) = read_pdb_file(filepath_pdb, radiustype) + + # ------------------------------------------------------------------------ + # MATERIAL PROPERTIES FOR ATOMS + + # The list that contains info about all types of atoms is created + # here. It is used for building the material properties for + # instance (see below). + atom_all_types_list = [] + + for atom in all_atoms: + FLAG_FOUND = False + for atom_type in atom_all_types_list: + # If the atom name is already in the list, FLAG on 'True'. + if atom_type[0] == atom.name: + FLAG_FOUND = True + break + # No name in the current list has been found? => New entry. + if FLAG_FOUND == False: + # Stored are: Atom label (e.g. 'Na'), the corresponding atom + # name (e.g. 'Sodium') and its color. + atom_all_types_list.append([atom.name, atom.element, atom.color]) + + # The list of materials is built. + # Note that all atoms of one type (e.g. all hydrogens) get only ONE + # material! This is good because then, by activating one atom in the + # Blender scene and changing the color of this atom, one changes the color + # of ALL atoms of the same type at the same time. + + # Create first a new list of materials for each type of atom + # (e.g. hydrogen) + for atom_type in atom_all_types_list: + material = bpy.data.materials.new(atom_type[1]) + material.name = atom_type[0] + material.diffuse_color = atom_type[2] + atom_material_list.append(material) + + # Now, we go through all atoms and give them a material. For all atoms ... + for atom in all_atoms: + # ... and all materials ... + for material in atom_material_list: + # ... select the correct material for the current atom via + # comparison of names ... + if atom.name in material.name: + # ... and give the atom its material properties. + # However, before we check, if it is a vacancy, because then it + # gets some additional preparation. The vacancy is represented + # by a transparent cube. + if atom.name == "Vacancy": + material.metallic = 0.8 + material.specular_intensity = 0.5 + material.roughness = 0.3 + material.blend_method = 'ADD' + material.show_transparent_back = False + # Some properties for cycles + material.use_nodes = True + mat_P_BSDF = material.node_tree.nodes['Principled BSDF'] + mat_P_BSDF.inputs['Metallic'].default_value = 0.1 + mat_P_BSDF.inputs['Roughness'].default_value = 0.2 + mat_P_BSDF.inputs['Transmission'].default_value = 0.97 + mat_P_BSDF.inputs['IOR'].default_value = 0.8 + # The atom gets its properties. + atom.material = material + + # ------------------------------------------------------------------------ + # READING DATA OF STICKS + + all_sticks = read_pdb_file_sticks(filepath_pdb, + use_sticks_bonds, + all_atoms) + # + # So far, all atoms, sticks and materials have been registered. + # + + # ------------------------------------------------------------------------ + # TRANSLATION OF THE STRUCTURE TO THE ORIGIN + + # It may happen that the structure in a PDB file already has an offset + # If chosen, the structure is first put into the center of the scene + # (the offset is subtracted). + + if put_to_center == True: + sum_vec = Vector((0.0,0.0,0.0)) + # Sum of all atom coordinates + sum_vec = sum([atom.location for atom in all_atoms], sum_vec) + # Then the average is taken + sum_vec = sum_vec / Number_of_total_atoms + # After, for each atom the center of gravity is subtracted + for atom in all_atoms: + atom.location -= sum_vec + + # ------------------------------------------------------------------------ + # SCALING + + # Take all atoms and adjust their radii and scale the distances. + for atom in all_atoms: + atom.location *= Ball_distance_factor + + # ------------------------------------------------------------------------ + # DETERMINATION OF SOME GEOMETRIC PROPERTIES + + # In the following, some geometric properties of the whole object are + # determined: center, size, etc. + sum_vec = Vector((0.0,0.0,0.0)) + + # First the center is determined. All coordinates are summed up ... + sum_vec = sum([atom.location for atom in all_atoms], sum_vec) + + # ... and the average is taken. This gives the center of the object. + object_center_vec = sum_vec / Number_of_total_atoms + + # Now, we determine the size.The farthest atom from the object center is + # taken as a measure. The size is used to place well the camera and light + # into the scene. + object_size_vec = [atom.location - object_center_vec for atom in all_atoms] + object_size = max(object_size_vec).length + + # ------------------------------------------------------------------------ + # SORTING THE ATOMS + + # Lists of atoms of one type are created. Example: + # draw_all_atoms = [ data_hydrogen,data_carbon,data_nitrogen ] + # data_hydrogen = [["Hydrogen", Material_Hydrogen, Vector((x,y,z)), 109], ...] + + # Go through the list which contains all types of atoms. It is the list, + # which has been created on the top during reading the PDB file. + # Example: atom_all_types_list = ["hydrogen", "carbon", ...] + draw_all_atoms = [] + for atom_type in atom_all_types_list: + + # Don't draw 'TER atoms'. + if atom_type[0] == "TER": + continue + + # This is the draw list, which contains all atoms of one type (e.g. + # all hydrogens) ... + draw_all_atoms_type = [] + + # Go through all atoms ... + for atom in all_atoms: + # ... select the atoms of the considered type via comparison ... + if atom.name == atom_type[0]: + # ... and append them to the list 'draw_all_atoms_type'. + draw_all_atoms_type.append([atom.name, + atom.material, + atom.location, + atom.radius]) + + # Now append the atom list to the list of all types of atoms + draw_all_atoms.append(draw_all_atoms_type) + + # ------------------------------------------------------------------------ + # COLLECTION + + # Before we start to draw the atoms and sticks, we first create a + # collection for the molecule. All atoms (balls) and sticks (cylinders) + # are put into this collection. + coll_molecule_name = os.path.basename(filepath_pdb) + scene = bpy.context.scene + coll_molecule = bpy.data.collections.new(coll_molecule_name) + scene.collection.children.link(coll_molecule) + + # ------------------------------------------------------------------------ + # DRAWING THE ATOMS + + bpy.ops.object.select_all(action='DESELECT') + + list_coll_elements = [] + # For each list of atoms of ONE type (e.g. Hydrogen) + for draw_all_atoms_type in draw_all_atoms: + + atom_mesh, coll_element = draw_atoms_one_type(draw_all_atoms_type, + Ball_type, + Ball_azimuth, + Ball_zenith, + Ball_radius_factor, + object_center_vec, + coll_molecule) + atom_object_list.append(atom_mesh) + list_coll_elements.append(coll_element) + + # ------------------------------------------------------------------------ + # DRAWING THE STICKS: cylinders in a dupliverts structure + + if use_sticks == True and use_sticks_type == '0' and all_sticks != []: + + sticks = draw_sticks_dupliverts(all_atoms, + atom_all_types_list, + object_center_vec, + all_sticks, + Stick_diameter, + Stick_sectors, + Stick_unit, + Stick_dist, + use_sticks_smooth, + use_sticks_color, + list_coll_elements) + for stick in sticks: + atom_object_list.append(stick) + + # ------------------------------------------------------------------------ + # DRAWING THE STICKS: skin and subdivision modifier + + if use_sticks == True and use_sticks_type == '1' and all_sticks != []: + + sticks = draw_sticks_skin(all_atoms, + all_sticks, + Stick_diameter, + use_sticks_smooth, + sticks_subdiv_view, + sticks_subdiv_render, + coll_molecule) + atom_object_list.append(sticks) + + # ------------------------------------------------------------------------ + # DRAWING THE STICKS: normal cylinders + + if use_sticks == True and use_sticks_type == '2' and all_sticks != []: + + sticks = draw_sticks_normal(all_atoms, + all_sticks, + object_center_vec, + Stick_diameter, + Stick_sectors, + use_sticks_smooth, + use_sticks_one_object, + use_sticks_one_object_nr, + coll_molecule) + atom_object_list.append(sticks) + + # ------------------------------------------------------------------------ + # CAMERA and LIGHT SOURCES + + camera_light_source(use_camera, + use_light, + object_center_vec, + object_size) + + # ------------------------------------------------------------------------ + # SELECT ALL LOADED OBJECTS + bpy.ops.object.select_all(action='DESELECT') + obj = None + for obj in atom_object_list: + obj.select_set(True) + + # activate the last selected object + if obj: + bpy.context.view_layer.objects.active = obj diff --git a/io_mesh_atomic/utility_gui.py b/io_mesh_atomic/utility_gui.py new file mode 100644 index 00000000..a9f7cb6d --- /dev/null +++ b/io_mesh_atomic/utility_gui.py @@ -0,0 +1,422 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +import bpy +from bpy.types import Operator, Panel +from bpy.props import (StringProperty, + EnumProperty, + FloatProperty, + BoolProperty) + +from io_mesh_atomic.utility_panel import choose_objects +from io_mesh_atomic.utility_panel import custom_datafile +from io_mesh_atomic.utility_panel import custom_datafile_change_atom_props +from io_mesh_atomic.utility_panel import separate_atoms +from io_mesh_atomic.utility_panel import distance + + +# ----------------------------------------------------------------------------- +# GUI + +# The panel. +class PANEL_PT_prepare(Panel): + bl_label = "Atomic Blender Utilities" + bl_space_type = "VIEW_3D" + bl_region_type = "UI" + bl_options = {'DEFAULT_CLOSED'} + bl_context = "objectmode" + bl_category = "Tools" + bl_idname = "ATOMIC_PT_utilities" + + + # This thing here just guarantees that the panel is NOT opened when the + # check box in the addon preferences is not activated! See __init__.py + @classmethod + def poll(cls, context): + pref = context.preferences + return pref.addons[__package__].preferences.bool_utility + + def draw(self, context): + layout = self.layout + scn = context.scene.atom_blend + + box = layout.box() + col = box.column(align=True) + col.label(text="Custom data file") + col.prop(scn, "datafile") + col.operator("atom_blend.datafile_apply") + + box = layout.box() + col = box.column(align=True) + col.label(text="Measure distances") + col.operator("atom_blend.button_distance") + col.prop(scn, "distance") + + # This is from Blender 2.79 and does not work in 2.80. However, it + # might be useful later on if changed. + # + #box = layout.box() + #col = box.column(align=True) + #col.label(text="All changes concern:") + #col.prop(scn, "action_type") + + box = layout.box() + col = box.column(align=True) + col.label(text="Change atom size") + col.label(text="1. Type of radii") + col.prop(scn, "radius_type") + col2 = col.column() + col2.active = (scn.radius_type == '3') + col2.prop(scn, "radius_type_ionic") + col = box.column(align=True) + col.label(text="2. Radii in pm") + col.prop(scn, "radius_pm_name") + col.prop(scn, "radius_pm") + col = box.column(align=True) + col.label(text="3. Radii by scale") + col.prop(scn, "radius_all") + row = col.row() + row.operator("atom_blend.radius_all_smaller") + row.operator("atom_blend.radius_all_bigger") + + box = layout.box() + col = box.column(align=True) + col.label(text="Change stick size") + col.prop(scn, "sticks_all") + row = col.row() + row.operator("atom_blend.sticks_all_smaller") + row.operator("atom_blend.sticks_all_bigger") + + box = layout.box() + col = box.column(align=True) + col.label(text="Change atom shape") + col2 = col.column() + col2.active = (scn.replace_objs_special == '0') + col2.prop(scn, "replace_objs") + col2.prop(scn, "replace_objs_material") + col.prop(scn, "replace_objs_special") + col.operator("atom_blend.replace_atom") + col.label(text="Default values") + col.operator("atom_blend.default_atoms") + + box = layout.box() + col = box.column(align=True) + col.label(text="Separate atoms") + col3 = col.column() + col3.active = (bpy.context.mode == 'EDIT_MESH') + col3.operator("atom_blend.separate_atom") + + +# The properties of buttons etc. in the panel. +class PanelProperties(bpy.types.PropertyGroup): + + def Callback_radius_type(self, context): + scn = bpy.context.scene.atom_blend + choose_objects("ATOM_RADIUS_TYPE", + scn.action_type, + None, + None, + scn.radius_type, + scn.radius_type_ionic, + None) + def Callback_radius_pm(self, context): + scn = bpy.context.scene.atom_blend + choose_objects("ATOM_RADIUS_PM", + scn.action_type, + None, + [scn.radius_pm_name, + scn.radius_pm], + None, + None, + None) + + datafile: StringProperty( + name = "", description="Path to your custom data file", + maxlen = 256, default = "", subtype='FILE_PATH') + XYZ_file: StringProperty( + name = "Path to file", default="", + description = "Path of the XYZ file") + number_atoms: StringProperty(name="", + default="Number", description = "This output shows " + "the number of atoms which have been loaded") + distance: StringProperty( + name="", default="Distance (A)", + description="Distance of 2 objects in Angstrom") + replace_objs: EnumProperty( + name="Shape", + description="Choose a different atom shape.", + items=(('0',"Unchanged", "Do not change the shape"), + ('1a',"Sphere (Mesh)", "Replace with a sphere (Mesh)"), + ('1b',"Sphere (NURBS)", "Replace with a sphere (NURBS)"), + ('2',"Cube", "Replace with a cube"), + ('3',"Plane", "Replace with a plane"), + ('4a',"Circle (Mesh)", "Replace with a circle (Mesh)"), + ('4b',"Circle (NURBS)", "Replace with a circle (NURBS)"), + ('5a',"Icosphere 1", "Replace with a icosphere, subd=1"), + ('5b',"Icosphere 2", "Replace with a icosphere, subd=2"), + ('5c',"Icosphere 3", "Replace with a icosphere, subd=3"), + ('5d',"Icosphere 4", "Replace with a icosphere, subd=4"), + ('5e',"Icosphere 5", "Replace with a icosphere, subd=5"), + ('6a',"Cylinder (Mesh)", "Replace with a cylinder (Mesh)"), + ('6b',"Cylinder (NURBS)", "Replace with a cylinder (NURBS)"), + ('7',"Cone", "Replace with a cone"), + ('8a',"Torus (Mesh)", "Replace with a torus (Mesh)"), + ('8b',"Torus (NURBS)", "Replace with a torus (NURBS)")), + default='0',) + replace_objs_material: EnumProperty( + name="Material", + description="Choose a different material.", + items=(('0',"Unchanged", "Leave the material unchanged"), + ('1',"Normal", "Use normal material (no transparency and reflection)"), + ('2',"Transparent", "Use transparent material"), + ('3',"Reflecting", "Use reflecting material"), + ('4',"Transparent + reflecting", "Use transparent and reflecting material")), + default='0',) + replace_objs_special: EnumProperty( + name="Special", + description="Choose a special atom shape.", + items=(('0',"None", "Use no special shape."), + ('1',"F2+ center", "Replace with a F2+ center"), + ('2',"F+ center", "Replace with a F+ center"), + ('3',"F0 center", "Replace with a F0 center")), + default='0',) + action_type: EnumProperty( + name="", + description="Which objects shall be modified?", + items=(('ALL_ACTIVE',"all active objects", "in the current layer"), + ('ALL_IN_LAYER',"all in all selected layers", + "in selected layer(s)")), + default='ALL_ACTIVE',) + radius_type: EnumProperty( + name="Type", + description="Which type of atom radii?", + items=(('0',"predefined", "Use pre-defined radii"), + ('1',"atomic", "Use atomic radii"), + ('2',"van der Waals","Use van der Waals radii"), + ('3',"ionic radii", "Use ionic radii")), + default='0',update=Callback_radius_type) + radius_type_ionic: EnumProperty( + name="Charge", + description="Charge state of the ions if existing.", + items=(('0',"-4", "Charge state -4"), + ('1',"-3", "Charge state -3"), + ('2',"-2", "Charge state -2"), + ('3',"-1", "Charge state -1"), + ('4'," 0", "Charge state 0: nothing is done"), + ('5',"+1", "Charge state +1"), + ('6',"+2", "Charge state +2"), + ('7',"+3", "Charge state +3"), + ('8',"+4", "Charge state +4"), + ('9',"+5", "Charge state +5"), + ('10',"+6", "Charge state +6"), + ('11',"+7", "Charge state +7")), + default='4',update=Callback_radius_type) + radius_pm_name: StringProperty( + name="", default="Atom name", + description="Put in the name of the atom (e.g. Hydrogen)") + radius_pm: FloatProperty( + name="", default=100.0, min=0.0, + description="Put in the radius of the atom (in pm)", + update=Callback_radius_pm) + radius_all: FloatProperty( + name="Scale", default = 1.05, min=1.0, max=5.0, + description="Put in the scale factor") + sticks_all: FloatProperty( + name="Scale", default = 1.05, min=1.0, max=5.0, + description="Put in the scale factor") + + +# Button loading a custom data file +class DatafileApply(Operator): + bl_idname = "atom_blend.datafile_apply" + bl_label = "Apply" + bl_description = "Use color and radii values stored in the custom file" + + def execute(self, context): + scn = bpy.context.scene.atom_blend + + if scn.datafile == "": + return {'FINISHED'} + + custom_datafile(scn.datafile) + custom_datafile_change_atom_props() + + return {'FINISHED'} + + +# Button for separating single atoms from a dupliverts structure +class DefaultAtom(Operator): + bl_idname = "atom_blend.default_atoms" + bl_label = "Default" + bl_description = ("Use default shapes and colors for atoms.") + + # Are we in the OBJECT mode? + @classmethod + def poll(self, context): + if bpy.context.mode == 'OBJECT': + return True + else: + return False + + def execute(self, context): + scn = bpy.context.scene.atom_blend + choose_objects("ATOM_DEFAULT_OBJ", + scn.action_type, + None, + None, + None, + None, + None) + return {'FINISHED'} + + +# Button for separating single atoms from a dupliverts structure +class ReplaceAtom(Operator): + bl_idname = "atom_blend.replace_atom" + bl_label = "Replace" + bl_description = ("Replace selected atoms with atoms of different shape.") + + # Are we in the OBJECT mode? + @classmethod + def poll(self, context): + if bpy.context.mode == 'OBJECT': + return True + else: + return False + + def execute(self, context): + scn = bpy.context.scene.atom_blend + choose_objects("ATOM_REPLACE_OBJ", + scn.action_type, + None, + None, + None, + None, + None) + return {'FINISHED'} + + +# Button for separating single atoms from a dupliverts structure +class SeparateAtom(Operator): + bl_idname = "atom_blend.separate_atom" + bl_label = "Separate" + bl_description = ("Separate selected atoms in a dupliverts structure. " + "You have to be in the 'Edit Mode'") + + # Are we in the EDIT mode? + @classmethod + def poll(self, context): + if bpy.context.mode == 'EDIT_MESH': + return True + else: + return False + + def execute(self, context): + scn = bpy.context.scene.atom_blend + + separate_atoms(scn) + + return {'FINISHED'} + + +# Button for measuring the distance of active objects +class DistanceButton(Operator): + bl_idname = "atom_blend.button_distance" + bl_label = "Measure ..." + bl_description = "Measure the distance between two atoms (objects)." + + def execute(self, context): + scn = bpy.context.scene.atom_blend + dist = distance() + + # Put the distance into the string of the output field. + scn.distance = dist + return {'FINISHED'} + + +# Button for increasing the radii of all selected atoms +class RadiusAllBiggerButton(Operator): + bl_idname = "atom_blend.radius_all_bigger" + bl_label = "Bigger ..." + bl_description = "Increase the radii of selected atoms" + + def execute(self, context): + scn = bpy.context.scene.atom_blend + choose_objects("ATOM_RADIUS_ALL", + scn.action_type, + scn.radius_all, + None, + None, + None, + None) + return {'FINISHED'} + + +# Button for decreasing the radii of all selected atoms +class RadiusAllSmallerButton(Operator): + bl_idname = "atom_blend.radius_all_smaller" + bl_label = "Smaller ..." + bl_description = "Decrease the radii of selected atoms" + + def execute(self, context): + scn = bpy.context.scene.atom_blend + choose_objects("ATOM_RADIUS_ALL", + scn.action_type, + 1.0/scn.radius_all, + None, + None, + None, + None) + return {'FINISHED'} + + +# Button for increasing the radii of all selected sticks +class SticksAllBiggerButton(Operator): + bl_idname = "atom_blend.sticks_all_bigger" + bl_label = "Bigger ..." + bl_description = "Increase the radii of selected sticks" + + def execute(self, context): + scn = bpy.context.scene.atom_blend + choose_objects("STICKS_RADIUS_ALL", + scn.action_type, + None, + None, + None, + None, + scn.sticks_all) + return {'FINISHED'} + + +# Button for decreasing the radii of all selected sticks +class SticksAllSmallerButton(Operator): + bl_idname = "atom_blend.sticks_all_smaller" + bl_label = "Smaller ..." + bl_description = "Decrease the radii of selected sticks" + + def execute(self, context): + scn = bpy.context.scene.atom_blend + choose_objects("STICKS_RADIUS_ALL", + scn.action_type, + None, + None, + None, + None, + 1.0/scn.sticks_all) + return {'FINISHED'} + diff --git a/io_mesh_atomic/utility_panel.py b/io_mesh_atomic/utility_panel.py new file mode 100644 index 00000000..5e85c0c8 --- /dev/null +++ b/io_mesh_atomic/utility_panel.py @@ -0,0 +1,1074 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +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') + def __init__(self, number, name, short_name, color, radii, radii_ionic): + self.number = number + self.name = name + self.short_name = short_name + self.color = color + self.radii = radii + self.radii_ionic = radii_ionic + + +# 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_bases) > 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, + who, + radius_all, + radius_pm, + radius_type, + radius_type_ionic, + sticks_all): + + # For selected objects of all selected layers + if who == "ALL_IN_LAYER": + # Determine all selected layers. + layers = [] + for i, layer in enumerate(bpy.context.scene.layers): + if layer == True: + layers.append(i) + + # Put all objects, which are in the layers, into a list. + change_objects_all = [] + for atom in bpy.context.scene.objects: + for layer in layers: + if atom.layers[layer] == True: + change_objects_all.append(atom) + # For selected objects of the visible layer + elif who == "ALL_ACTIVE": + change_objects_all = [] + # Note all selected objects first. + 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())): + + # 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). + 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). + atom.hide_set(True) + bpy.context.view_layer.objects.active = None + + # Replace atom objects + if action_type == "ATOM_REPLACE_OBJ" and "STICK" not in atom.name.upper(): + + scn = bpy.context.scene.atom_blend + + new_material = draw_obj_material(scn.replace_objs_material, + atom.active_material) + + # Special object (like halo, etc.) + if scn.replace_objs_special != '0': + new_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 + #return {'FINISHED'} + # If the atom shape shall change, then: + else: + new_atom = draw_obj(scn.replace_objs, atom, new_material) + + # Default shapes and colors for atoms + if action_type == "ATOM_DEFAULT_OBJ" and "STICK" not in atom.name.upper(): + + scn = bpy.context.scene.atom_blend + + # Create new material + new_material = bpy.data.materials.new("tmp") + # Create new object (NURBS sphere = '1b') + new_atom = draw_obj('1b', atom, new_material) + new_atom.active_material = new_material + new_material = draw_obj_material('0', new_material) + + # Change size and color of the new object + for element in ELEMENTS: + if element.name in new_atom.name: + new_atom.scale = (element.radii[0],) * 3 + new_atom.active_material.diffuse_color = element.color + new_atom.name = element.name + "_ball" + new_atom.active_material.name = element.name + break + + +# Separating atoms from a dupliverts strucutre. +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() + del(bm) + + # 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" + + 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): + + if material_type == '0': # Unchanged + material_new = material + if material_type == '1': # Normal + material_new = bpy.data.materials.new(material.name + "_normal") + if material_type == '2': # Transparent + material_new = bpy.data.materials.new(material.name + "_transparent") + material_new.metallic = 0.8 + material_new.specular_intensity = 0.5 + material_new.roughness = 0.3 + material_new.blend_method = 'ADD' + material_new.show_transparent_back = False + # Some properties for cycles + material_new.use_nodes = True + mat_P_BSDF = material_new.node_tree.nodes['Principled BSDF'] + mat_P_BSDF.inputs['Metallic'].default_value = 0.1 + mat_P_BSDF.inputs['Roughness'].default_value = 0.2 + mat_P_BSDF.inputs['Transmission'].default_value = 0.9 + mat_P_BSDF.inputs['IOR'].default_value = 0.8 + if material_type == '3': # Reflecting + material_new = bpy.data.materials.new(material.name + "_reflecting") + material_new.metallic = 0.5 + material_new.specular_intensity = 0.5 + material_new.roughness = 0.0 + material_new.blend_method = 'OPAQUE' + # Some properties for cycles + material_new.use_nodes = True + mat_P_BSDF = material_new.node_tree.nodes['Principled BSDF'] + mat_P_BSDF.inputs['Metallic'].default_value = 0.95 + mat_P_BSDF.inputs['Roughness'].default_value = 0.1 + mat_P_BSDF.inputs['Transmission'].default_value = 0.0 + mat_P_BSDF.inputs['IOR'].default_value = 1.0 + if material_type == '4': # Transparent + reflecting + material_new = bpy.data.materials.new(material.name + "_trans+refl") + material_new.metallic = 0.3 + material_new.specular_intensity = 0.5 + material_new.roughness = 0.3 + material_new.blend_method = 'ADD' + material_new.show_transparent_back = False + # Some properties for cycles + material_new.use_nodes = True + mat_P_BSDF = material_new.node_tree.nodes['Principled BSDF'] + mat_P_BSDF.inputs['Metallic'].default_value = 0.5 + mat_P_BSDF.inputs['Roughness'].default_value = 0.2 + mat_P_BSDF.inputs['Transmission'].default_value = 0.5 + mat_P_BSDF.inputs['IOR'].default_value = 0.8 + + # 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 + + +# 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, + view_align=False, + enter_editmode=False, + location=atom.location, + rotation=(0, 0, 0)) + if atom_shape == '1b': #Sphere NURBS + bpy.ops.surface.primitive_nurbs_surface_sphere_add( + view_align=False, + enter_editmode=False, + location=atom.location, + rotation=(0.0, 0.0, 0.0)) + if atom_shape == '2': #Cube + bpy.ops.mesh.primitive_cube_add( + view_align=False, + enter_editmode=False, + location=atom.location, + rotation=(0.0, 0.0, 0.0)) + if atom_shape == '3': #Plane + bpy.ops.mesh.primitive_plane_add( + view_align=False, + 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', + view_align=False, + enter_editmode=False, + location=atom.location, + rotation=(0, 0, 0)) + if atom_shape == '4b': #Circle NURBS + bpy.ops.surface.primitive_nurbs_surface_circle_add( + view_align=False, + 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, + view_align=False, + 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', + view_align=False, + enter_editmode=False, + location=atom.location, + rotation=(0, 0, 0)) + if atom_shape == '6b': #Cylinder NURBS + bpy.ops.surface.primitive_nurbs_surface_cylinder_add( + view_align=False, + 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', + view_align=False, + 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, + view_align=False, + 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( + view_align=False, + 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() + del(child) + + # 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() + del(atom) + + #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) + + + # F2+ center + if atom_shape == '1': + # Create first a cube + bpy.ops.mesh.primitive_cube_add(view_align=False, + 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.name = atom.name + "_F2+_vac" + cube.select_set(True) + # New material for this cube + material_cube = bpy.data.materials.new(atom.name + "_F2+_vac") + material_cube.diffuse_color = [0.8, 0.0, 0.0, 1.0] + material_cube.metallic = 0.8 + material_cube.specular_intensity = 0.5 + material_cube.roughness = 0.3 + material_cube.blend_method = 'ADD' + material_cube.show_transparent_back = True + # Some properties for cycles + material_cube.use_nodes = True + mat_P_BSDF = material_cube.node_tree.nodes['Principled BSDF'] + mat_P_BSDF.inputs['Metallic'].default_value = 0.1 + mat_P_BSDF.inputs['Roughness'].default_value = 0.2 + mat_P_BSDF.inputs['Transmission'].default_value = 0.9 + mat_P_BSDF.inputs['IOR'].default_value = 0.8 + cube.active_material = material_cube + # Put a nice 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 = 1.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 + # Some properties for cycles + lamp.data.use_nodes = True + lmp_P_BSDF = lamp.data.node_tree.nodes['Emission'] + lmp_P_BSDF.inputs['Strength'].default_value = 2000 + # 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': + # Create first a cube + bpy.ops.mesh.primitive_cube_add(view_align=False, + 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.name = atom.name + "_F+_vac" + cube.select_set(True) + # New material for this cube + material_cube = bpy.data.materials.new(atom.name + "_F+_vac") + material_cube.diffuse_color = [0.0, 0.0, 0.8, 1.0] + material_cube.metallic = 0.8 + material_cube.specular_intensity = 0.5 + material_cube.roughness = 0.3 + material_cube.blend_method = 'ADD' + material_cube.show_transparent_back = True + # Some properties for cycles + material_cube.use_nodes = True + mat_P_BSDF = material_cube.node_tree.nodes['Principled BSDF'] + mat_P_BSDF.inputs['Metallic'].default_value = 0.1 + mat_P_BSDF.inputs['Roughness'].default_value = 0.2 + mat_P_BSDF.inputs['Transmission'].default_value = 0.9 + mat_P_BSDF.inputs['IOR'].default_value = 0.8 + cube.active_material = material_cube + # Create now an electron + scale = atom.scale / 10.0 + bpy.ops.surface.primitive_nurbs_surface_sphere_add( + view_align=False, + 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.diffuse_color = [0.0, 0.0, 0.8, 1.0] + material_electron.metallic = 0.8 + material_electron.specular_intensity = 0.5 + material_electron.roughness = 0.3 + material_electron.blend_method = 'OPAQUE' + material_electron.show_transparent_back = False + electron.active_material = material_electron + # Put a nice 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 = 1.0 + lamp_data.color = (0.8, 0.8, 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 + # Some properties for cycles + lamp.data.use_nodes = True + lmp_P_BSDF = lamp.data.node_tree.nodes['Emission'] + lmp_P_BSDF.inputs['Strength'].default_value = 2000 + # 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': + # Create first a cube + bpy.ops.mesh.primitive_cube_add(view_align=False, + 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.name = atom.name + "_F0_vac" + cube.select_set(True) + # New material for this cube + material_cube = bpy.data.materials.new(atom.name + "_F0_vac") + material_cube.diffuse_color = [0.8, 0.8, 0.8, 1.0] + material_cube.metallic = 0.8 + material_cube.specular_intensity = 0.5 + material_cube.roughness = 0.83 + material_cube.blend_method = 'ADD' + material_cube.show_transparent_back = True + # Some properties for cycles + material_cube.use_nodes = True + mat_P_BSDF = material_cube.node_tree.nodes['Principled BSDF'] + mat_P_BSDF.inputs['Metallic'].default_value = 0.1 + mat_P_BSDF.inputs['Roughness'].default_value = 0.2 + mat_P_BSDF.inputs['Transmission'].default_value = 0.9 + mat_P_BSDF.inputs['IOR'].default_value = 0.8 + cube.active_material = material_cube + # Create now two electrons + scale = atom.scale / 10.0 + bpy.ops.surface.primitive_nurbs_surface_sphere_add( + view_align=False, + 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_electron1" + electron1.parent = cube + bpy.ops.surface.primitive_nurbs_surface_sphere_add( + view_align=False, + 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_electron2" + electron2.parent = cube + # New material for the electrons + material_electron = bpy.data.materials.new(atom.name + "_F0-center") + material_electron.diffuse_color = [0.0, 0.0, 0.8, 1.0] + material_electron.metallic = 0.8 + material_electron.specular_intensity = 0.5 + material_electron.roughness = 0.3 + material_electron.blend_method = 'OPAQUE' + material_electron.show_transparent_back = False + electron1.active_material = material_electron + electron2.active_material = material_electron + # Put two nice point lamps inside the electrons + lamp1_data = bpy.data.lights.new(name=atom.name + "_F0_lamp1", + type="POINT") + lamp1_data.distance = atom.scale[0] * 2.0 + lamp1_data.energy = 1.0 + lamp1_data.color = (0.8, 0.8, 0.8) + 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_lamp2", + type="POINT") + lamp2_data.distance = atom.scale[0] * 2.0 + lamp2_data.energy = 1.0 + lamp2_data.color = (0.8, 0.8, 0.8) + 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 + # Some properties for cycles + lamp1.data.use_nodes = True + lamp2.data.use_nodes = True + lmp1_P_BSDF = lamp1.data.node_tree.nodes['Emission'] + lmp2_P_BSDF = lamp1.data.node_tree.nodes['Emission'] + lmp1_P_BSDF.inputs['Strength'].default_value = 200 + lmp2_P_BSDF.inputs['Strength'].default_value = 200 + # 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() + del(atom) + + 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: + 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: + child.scale = (element.radii[0],) * 3 + child.active_material.diffuse_color = element.color + else: + if atom.type in {'SURFACE', 'MESH', 'META'}: + for element in ELEMENTS: + if element.name in atom.name: + atom.scale = (element.radii[0],) * 3 + atom.active_material.diffuse_color = element.color + + +# 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 "Atom" in line: + + line = data_file_p.readline() + # Number + line = data_file_p.readline() + number = line[19:-1] + # Name + line = data_file_p.readline() + name = line[19:-1] + # Short name + line = data_file_p.readline() + short_name = line[19:-1] + # Color + line = data_file_p.readline() + color_value = line[19:-1].split(',') + color = [float(color_value[0]), + float(color_value[1]), + float(color_value[2]), + float(color_value[3])] + # Used radius + line = data_file_p.readline() + radius_used = float(line[19:-1]) + # Atomic radius + line = data_file_p.readline() + radius_atomic = float(line[19:-1]) + # Van der Waals radius + line = data_file_p.readline() + radius_vdW = float(line[19:-1]) + radii = [radius_used,radius_atomic,radius_vdW] + radii_ionic = [] + + element = ElementProp(number,name,short_name,color, + radii, radii_ionic) + + ELEMENTS.append(element) + + data_file_p.close() + + return True diff --git a/io_mesh_atomic/xyz_export.py b/io_mesh_atomic/xyz_export.py new file mode 100644 index 00000000..79e99736 --- /dev/null +++ b/io_mesh_atomic/xyz_export.py @@ -0,0 +1,83 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +import bpy +from io_mesh_atomic.xyz_import import ELEMENTS_DEFAULT + + +class AtomsExport(object): + __slots__ = ('element', 'location') + def __init__(self, element, location): + self.element = element + self.location = location + + +def export_xyz(obj_type, filepath_xyz): + + list_atoms = [] + counter = 0 + for obj in bpy.context.selected_objects: + + if "STICK" in obj.name.upper(): + continue + + if obj.type not in {'MESH', 'SURFACE', 'META'}: + continue + + name = "" + for element in ELEMENTS_DEFAULT: + if element[1] in obj.name: + if element[2] == "Vac": + name = "X" + else: + name = element[2] + + if name == "": + if obj_type == "0": + name = "?" + else: + continue + + if len(obj.children) != 0: + for vertex in obj.data.vertices: + location = obj.matrix_world @ vertex.co + list_atoms.append(AtomsExport(name, location)) + counter += 1 + else: + if not obj.parent: + location = obj.location + list_atoms.append(AtomsExport(name, location)) + counter += 1 + + xyz_file_p = open(filepath_xyz, "w") + xyz_file_p.write("%d\n" % counter) + xyz_file_p.write("This XYZ file has been created with Blender " + "and the addon Atomic Blender - XYZ. " + "For more details see the wiki pages of Blender.\n") + + for i, atom in enumerate(list_atoms): + string = "%3s%15.5f%15.5f%15.5f\n" % ( + atom.element, + atom.location[0], + atom.location[1], + atom.location[2]) + xyz_file_p.write(string) + + xyz_file_p.close() + + return True diff --git a/io_mesh_atomic/xyz_gui.py b/io_mesh_atomic/xyz_gui.py new file mode 100644 index 00000000..79b08f21 --- /dev/null +++ b/io_mesh_atomic/xyz_gui.py @@ -0,0 +1,210 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +import bpy +from bpy.types import Operator, AddonPreferences +from bpy_extras.io_utils import ImportHelper, ExportHelper +from bpy.props import ( + StringProperty, + BoolProperty, + EnumProperty, + IntProperty, + FloatProperty, + ) + +from io_mesh_atomic.xyz_import import import_xyz +from io_mesh_atomic.xyz_import import ALL_FRAMES +from io_mesh_atomic.xyz_import import ELEMENTS +from io_mesh_atomic.xyz_import import STRUCTURE +from io_mesh_atomic.xyz_import import build_frames +from io_mesh_atomic.xyz_export import export_xyz + +# ----------------------------------------------------------------------------- +# Operators + +# This is the class for the file dialog. +class IMPORT_OT_xyz(Operator, ImportHelper): + bl_idname = "import_mesh.xyz" + bl_label = "Import XYZ (*.xyz)" + bl_options = {'PRESET', 'UNDO'} + + filename_ext = ".xyz" + filter_glob: StringProperty(default="*.xyz", options={'HIDDEN'},) + + use_camera: BoolProperty( + name="Camera", default=False, + description="Do you need a camera?") + use_lamp: BoolProperty( + name="Lamp", default=False, + description = "Do you need a lamp?") + ball: EnumProperty( + name="Type of ball", + description="Choose ball", + items=(('0', "NURBS", "NURBS balls"), + ('1', "Mesh" , "Mesh balls"), + ('2', "Meta" , "Metaballs")), + default='0',) + mesh_azimuth: IntProperty( + name = "Azimuth", default=32, min=1, + description = "Number of sectors (azimuth)") + mesh_zenith: IntProperty( + name = "Zenith", default=32, min=1, + description = "Number of sectors (zenith)") + scale_ballradius: FloatProperty( + name = "Balls", default=1.0, min=0.0001, + description = "Scale factor for all atom radii") + scale_distances: FloatProperty ( + name = "Distances", default=1.0, min=0.0001, + description = "Scale factor for all distances") + atomradius: EnumProperty( + name="Type of radius", + description="Choose type of atom radius", + items=(('0', "Pre-defined", "Use pre-defined radius"), + ('1', "Atomic", "Use atomic radius"), + ('2', "van der Waals", "Use van der Waals radius")), + default='0',) + use_center: BoolProperty( + name = "Object to origin (first frames)", default=False, + description = "Put the object into the global origin, the first frame only") + use_center_all: BoolProperty( + name = "Object to origin (all frames)", default=True, + description = "Put the object into the global origin, all frames") + datafile: StringProperty( + name = "", description="Path to your custom data file", + maxlen = 256, default = "", subtype='FILE_PATH') + use_frames: BoolProperty( + name = "Load all frames?", default=False, + description = "Do you want to load all frames?") + skip_frames: IntProperty( + name="", default=0, min=0, + description="Number of frames you want to skip.") + images_per_key: IntProperty( + name="", default=1, min=1, + description="Choose the number of images between 2 keys.") + + # This thing here just guarantees that the menu entry is not active when the + # check box in the addon preferences is not activated! See __init__.py + @classmethod + def poll(cls, context): + pref = context.preferences + return pref.addons[__package__].preferences.bool_xyz + + def draw(self, context): + layout = self.layout + row = layout.row() + row.prop(self, "use_camera") + row.prop(self, "use_lamp") + row = layout.row() + col = row.column() + col.prop(self, "ball") + row = layout.row() + row.active = (self.ball == "1") + col = row.column(align=True) + col.prop(self, "mesh_azimuth") + col.prop(self, "mesh_zenith") + row = layout.row() + col = row.column() + col.label(text="Scaling factors") + col = row.column(align=True) + col.prop(self, "scale_ballradius") + col.prop(self, "scale_distances") + row = layout.row() + row.prop(self, "use_center") + row = layout.row() + row.prop(self, "use_center_all") + row = layout.row() + row.prop(self, "atomradius") + + row = layout.row() + row.prop(self, "use_frames") + row = layout.row() + row.active = self.use_frames + col = row.column() + col.label(text="Skip frames") + col = row.column() + col.prop(self, "skip_frames") + row = layout.row() + row.active = self.use_frames + col = row.column() + col.label(text="Frames/key") + col = row.column() + col.prop(self, "images_per_key") + + def execute(self, context): + + del ALL_FRAMES[:] + del ELEMENTS[:] + del STRUCTURE[:] + + # This is to determine the path. + filepath_xyz = bpy.path.abspath(self.filepath) + + # Execute main routine + import_xyz(self.ball, + self.mesh_azimuth, + self.mesh_zenith, + self.scale_ballradius, + self.atomradius, + self.scale_distances, + self.use_center, + self.use_center_all, + self.use_camera, + self.use_lamp, + filepath_xyz) + + # Load frames + if len(ALL_FRAMES) > 1 and self.use_frames: + + build_frames(self.images_per_key, self.skip_frames) + + return {'FINISHED'} + + +# This is the class for the file dialog of the exporter. +class EXPORT_OT_xyz(Operator, ExportHelper): + bl_idname = "export_mesh.xyz" + bl_label = "Export XYZ (*.xyz)" + filename_ext = ".xyz" + + filter_glob: StringProperty( + default="*.xyz", options={'HIDDEN'},) + + atom_xyz_export_type: EnumProperty( + name="Type of Objects", + description="Choose type of objects", + items=(('0', "All", "Export all active objects"), + ('1', "Elements", "Export only those active objects which have" + " a proper element name")), + default='1',) + + # This thing here just guarantees that the menu entry is not active when the + # check box in the addon preferences is not activated! See __init__.py + @classmethod + def poll(cls, context): + pref = context.preferences + return pref.addons[__package__].preferences.bool_xyz + + def draw(self, context): + layout = self.layout + row = layout.row() + row.prop(self, "atom_xyz_export_type") + + def execute(self, context): + export_xyz(self.atom_xyz_export_type, bpy.path.abspath(self.filepath)) + + return {'FINISHED'} diff --git a/io_mesh_atomic/xyz_import.py b/io_mesh_atomic/xyz_import.py new file mode 100644 index 00000000..fe903f4e --- /dev/null +++ b/io_mesh_atomic/xyz_import.py @@ -0,0 +1,810 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +import os +import bpy +from math import pi, sqrt +from mathutils import Vector, Matrix + +# ----------------------------------------------------------------------------- +# 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), +) + +# This list here contains all data of the elements and will be used during +# runtime. It is a list of classes. +# During executing Atomic Blender, the list will be initialized with the fixed +# data from above via the class structure below (ElementProp). We +# have then one fixed list (above), which will never be changed, and a list of +# classes with same data. The latter can be modified via loading a separate +# custom data file for instance. +ELEMENTS = [] + +# This is the list, which contains all atoms of all frames! Each item is a +# list which contains the atoms of a single frame. It is a list of +# 'AtomProp'. +ALL_FRAMES = [] + +# A list of ALL balls which are put into the scene +STRUCTURE = [] + + +# This is the class, which stores the properties for one element. +class ElementProp(object): + __slots__ = ('number', 'name', 'short_name', 'color', 'radii', 'radii_ionic') + def __init__(self, number, name, short_name, color, radii, radii_ionic): + self.number = number + self.name = name + self.short_name = short_name + self.color = color + self.radii = radii + self.radii_ionic = radii_ionic + +# This is the class, which stores the properties of one atom. +class AtomProp(object): + __slots__ = ('element', 'name', 'location', 'radius', 'color', 'material') + def __init__(self, element, name, location, radius, color, material): + self.element = element + self.name = name + self.location = location + self.radius = radius + self.color = color + self.material = material + + +# ----------------------------------------------------------------------------- +# Some basic routines + +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 = [] + + li = ElementProp(item[0],item[1],item[2],item[3], + radii,radii_ionic) + ELEMENTS.append(li) + + +# filepath_pdb: path to pdb file +# radiustype : '0' default +# '1' atomic radii +# '2' van der Waals +def read_xyz_file(filepath_xyz,radiustype): + + number_frames = 0 + total_number_atoms = 0 + + # Open the file ... + filepath_xyz_p = open(filepath_xyz, "r") + + #Go through the whole file. + FLAG = False + for line in filepath_xyz_p: + + # ... the loop is broken here (EOF) ... + if line == "": + continue + + split_list = line.rsplit() + + if len(split_list) == 1: + number_atoms = int(split_list[0]) + FLAG = True + + if FLAG == True: + + line = filepath_xyz_p.readline() + line = line.rstrip() + + all_atoms= [] + for i in range(number_atoms): + + + # This is a guarantee that only the total number of atoms of the + # first frame is used. Condition is, so far, that the number of + # atoms in a xyz file is constant. However, sometimes the number + # may increase (or decrease). If it decreases, the addon crashes. + # If it increases, only the tot number of atoms of the first frame + # is used. + # By time, I will allow varying atom numbers ... but this takes + # some time ... + if number_frames != 0: + if i >= total_number_atoms: + break + + + line = filepath_xyz_p.readline() + line = line.rstrip() + split_list = line.rsplit() + short_name = str(split_list[0]) + + # Go through all elements and find the element of the current atom. + FLAG_FOUND = False + for element in ELEMENTS: + if str.upper(short_name) == str.upper(element.short_name): + # Give the atom its proper name, color and radius: + name = element.name + # int(radiustype) => type of radius: + # pre-defined (0), atomic (1) or van der Waals (2) + radius = float(element.radii[int(radiustype)]) + color = element.color + FLAG_FOUND = True + break + + # Is it a vacancy or an 'unknown atom' ? + if FLAG_FOUND == False: + # Give this atom also a name. If it is an 'X' then it is a + # vacancy. Otherwise ... + if "X" in short_name: + short_name = "VAC" + name = "Vacancy" + radius = float(ELEMENTS[-3].radii[int(radiustype)]) + color = ELEMENTS[-3].color + # ... take what is written in the xyz file. These are somewhat + # unknown atoms. This should never happen, the element list is + # almost complete. However, we do this due to security reasons. + else: + name = str.upper(short_name) + radius = float(ELEMENTS[-2].radii[int(radiustype)]) + color = ELEMENTS[-2].color + + x = float(split_list[1]) + y = float(split_list[2]) + z = float(split_list[3]) + + location = Vector((x,y,z)) + + all_atoms.append([short_name, name, location, radius, color]) + + # We note here all elements. This needs to be done only once. + if number_frames == 0: + + # This is a guarantee that only the total number of atoms of the + # first frame is used. Condition is, so far, that the number of + # atoms in a xyz file is constant. However, sometimes the number + # may increase (or decrease). If it decreases, the addon crashes. + # If it increases, only the tot number of atoms of the first frame + # is used. + # By time, I will allow varying atom numbers ... but this takes + # some time ... + total_number_atoms = number_atoms + + + elements = [] + for atom in all_atoms: + FLAG_FOUND = False + for element in elements: + # If the atom name is already in the list, + # FLAG on 'True'. + if element == atom[1]: + FLAG_FOUND = True + break + # No name in the current list has been found? => New entry. + if FLAG_FOUND == False: + # Stored are: Atom label (e.g. 'Na'), the corresponding + # atom name (e.g. 'Sodium') and its color. + elements.append(atom[1]) + + # Sort the atoms: create lists of atoms of one type + structure = [] + for element in elements: + atoms_one_type = [] + for atom in all_atoms: + if atom[1] == element: + atoms_one_type.append(AtomProp(atom[0], + atom[1], + atom[2], + atom[3], + atom[4],[])) + structure.append(atoms_one_type) + + ALL_FRAMES.append(structure) + number_frames += 1 + FLAG = False + + filepath_xyz_p.close() + + return total_number_atoms + + +# Rotate an object. +def rotate_object(rot_mat, obj): + + bpy.ops.object.select_all(action='DESELECT') + obj.select_set(True) + + # Decompose world_matrix's components, and from them assemble 4x4 matrices. + orig_loc, orig_rot, orig_scale = obj.matrix_world.decompose() + + orig_loc_mat = Matrix.Translation(orig_loc) + orig_rot_mat = orig_rot.to_matrix().to_4x4() + orig_scale_mat = (Matrix.Scale(orig_scale[0],4,(1,0,0)) @ + Matrix.Scale(orig_scale[1],4,(0,1,0)) @ + Matrix.Scale(orig_scale[2],4,(0,0,1))) + + # Assemble the new matrix. + obj.matrix_world = orig_loc_mat @ rot_mat @ orig_rot_mat @ orig_scale_mat + + +# Function, which puts a camera and light source into the 3D scene +def camera_light_source(use_camera, + use_light, + object_center_vec, + object_size): + + camera_factor = 15.0 + + # If chosen a camera is put into the scene. + if use_camera == True: + + # Assume that the object is put into the global origin. Then, the + # camera is moved in x and z direction, not in y. The object has its + # size at distance sqrt(object_size) from the origin. So, move the + # camera by this distance times a factor of camera_factor in x and z. + # Then add x, y and z of the origin of the object. + object_camera_vec = Vector((sqrt(object_size) * camera_factor, + 0.0, + sqrt(object_size) * camera_factor)) + camera_xyz_vec = object_center_vec + object_camera_vec + + # Create the camera + camera_data = bpy.data.cameras.new("A_camera") + camera_data.lens = 45 + camera_data.clip_end = 500.0 + camera = bpy.data.objects.new("A_camera", camera_data) + camera.location = camera_xyz_vec + bpy.context.collection.objects.link(camera) + + # Here the camera is rotated such it looks towards the center of + # the object. The [0.0, 0.0, 1.0] vector along the z axis + z_axis_vec = Vector((0.0, 0.0, 1.0)) + # The angle between the last two vectors + angle = object_camera_vec.angle(z_axis_vec, 0) + # The cross-product of z_axis_vec and object_camera_vec + axis_vec = z_axis_vec.cross(object_camera_vec) + # Rotate 'axis_vec' by 'angle' and convert this to euler parameters. + # 4 is the size of the matrix. + camera.rotation_euler = Matrix.Rotation(angle, 4, axis_vec).to_euler() + + # Rotate the camera around its axis by 90° such that we have a nice + # camera position and view onto the object. + bpy.ops.object.select_all(action='DESELECT') + camera.select_set(True) + + # Rotate the camera around its axis 'object_camera_vec' by 90° such + # that we have a nice camera view onto the object. + matrix_rotation = Matrix.Rotation(90/360*2*pi, 4, object_camera_vec) + rotate_object(matrix_rotation, camera) + + # Here a lamp is put into the scene, if chosen. + if use_light == True: + + # This is the distance from the object measured in terms of % + # of the camera distance. It is set onto 50% (1/2) distance. + light_dl = sqrt(object_size) * 15 * 0.5 + # This is a factor to which extend the lamp shall go to the right + # (from the camera point of view). + light_dy_right = light_dl * (3.0/4.0) + + # Create x, y and z for the lamp. + object_light_vec = Vector((light_dl,light_dy_right,light_dl)) + light_xyz_vec = object_center_vec + object_light_vec + + # Create the lamp + light_data = bpy.data.lights.new(name="A_light", type="SUN") + light_data.distance = 500.0 + light_data.energy = 3.0 + lamp = bpy.data.objects.new("A_light", light_data) + lamp.location = light_xyz_vec + bpy.context.collection.objects.link(lamp) + + # Some settings for the World: a bit ambient occlusion + bpy.context.scene.world.light_settings.use_ambient_occlusion = True + bpy.context.scene.world.light_settings.ao_factor = 0.2 + # Some properties for cycles + lamp.data.use_nodes = True + lmp_P_BSDF = lamp.data.node_tree.nodes['Emission'] + lmp_P_BSDF.inputs['Strength'].default_value = 5 + +# ----------------------------------------------------------------------------- +# The main routine + +def import_xyz(Ball_type, + Ball_azimuth, + Ball_zenith, + Ball_radius_factor, + radiustype, + Ball_distance_factor, + put_to_center, + put_to_center_all, + use_camera, + use_light, + filepath_xyz): + + # List of materials + atom_material_list = [] + + # ------------------------------------------------------------------------ + # INITIALIZE THE ELEMENT LIST + + read_elements() + + # ------------------------------------------------------------------------ + # READING DATA OF ATOMS + + Number_of_total_atoms = read_xyz_file(filepath_xyz, radiustype) + + # We show the atoms of the first frame. + first_frame = ALL_FRAMES[0] + + # ------------------------------------------------------------------------ + # MATERIAL PROPERTIES FOR ATOMS + + # Create first a new list of materials for each type of atom + # (e.g. hydrogen) + for atoms_of_one_type in first_frame: + # Take the first atom + atom = atoms_of_one_type[0] + material = bpy.data.materials.new(atom.name) + material.name = atom.name + material.diffuse_color = atom.color + atom_material_list.append(material) + + # Now, we go through all atoms and give them a material. For all atoms ... + for atoms_of_one_type in first_frame: + for atom in atoms_of_one_type: + # ... and all materials ... + for material in atom_material_list: + # ... select the correct material for the current atom via + # comparison of names ... + if atom.name in material.name: + # ... and give the atom its material properties. + # However, before we check if it is a vacancy + # The vacancy is represented by a transparent cube. + if atom.name == "Vacancy": + material.metallic = 0.8 + material.specular_intensity = 0.5 + material.roughness = 0.3 + material.blend_method = 'ADD' + material.show_transparent_back = False + # Some properties for cycles + material.use_nodes = True + mat_P_BSDF = material.node_tree.nodes['Principled BSDF'] + mat_P_BSDF.inputs['Metallic'].default_value = 0.1 + mat_P_BSDF.inputs['Roughness'].default_value = 0.2 + mat_P_BSDF.inputs['Transmission'].default_value = 0.97 + mat_P_BSDF.inputs['IOR'].default_value = 0.8 + # The atom gets its properties. + atom.material = material + + # ------------------------------------------------------------------------ + # TRANSLATION OF THE STRUCTURE TO THE ORIGIN + + # It may happen that the structure in a XYZ file already has an offset + + + # If chosen, the structure is put into the center of the scene + # (only the first frame). + if put_to_center == True and put_to_center_all == False: + + sum_vec = Vector((0.0,0.0,0.0)) + + # Sum of all atom coordinates + for atoms_of_one_type in first_frame: + sum_vec = sum([atom.location for atom in atoms_of_one_type], sum_vec) + + # Then the average is taken + sum_vec = sum_vec / Number_of_total_atoms + + # After, for each atom the center of gravity is substracted + for atoms_of_one_type in first_frame: + for atom in atoms_of_one_type: + atom.location -= sum_vec + + # If chosen, the structure is put into the center of the scene + # (all frames). + if put_to_center_all == True: + + # For all frames + for frame in ALL_FRAMES: + + sum_vec = Vector((0.0,0.0,0.0)) + + # Sum of all atom coordinates + for (i, atoms_of_one_type) in enumerate(frame): + + # This is a guarantee that only the total number of atoms of the + # first frame is used. Condition is, so far, that the number of + # atoms in a xyz file is constant. However, sometimes the number + # may increase (or decrease). If it decreases, the addon crashes. + # If it increases, only the tot number of atoms of the first frame + # is used. + # By time, I will allow varying atom numbers ... but this takes + # some time ... + if i >= Number_of_total_atoms: + break + + sum_vec = sum([atom.location for atom in atoms_of_one_type], sum_vec) + + # Then the average is taken + sum_vec = sum_vec / Number_of_total_atoms + + # After, for each atom the center of gravity is substracted + for atoms_of_one_type in frame: + for atom in atoms_of_one_type: + atom.location -= sum_vec + + + # ------------------------------------------------------------------------ + # SCALING + + # Take all atoms and adjust their radii and scale the distances. + for atoms_of_one_type in first_frame: + for atom in atoms_of_one_type: + atom.location *= Ball_distance_factor + + # ------------------------------------------------------------------------ + # DETERMINATION OF SOME GEOMETRIC PROPERTIES + + # In the following, some geometric properties of the whole object are + # determined: center, size, etc. + sum_vec = Vector((0.0,0.0,0.0)) + + # First the center is determined. All coordinates are summed up ... + for atoms_of_one_type in first_frame: + sum_vec = sum([atom.location for atom in atoms_of_one_type], sum_vec) + + # ... and the average is taken. This gives the center of the object. + object_center_vec = sum_vec / Number_of_total_atoms + + # Now, we determine the size.The farthest atom from the object center is + # taken as a measure. The size is used to place well the camera and light + # into the scene. + + object_size_vec = [] + for atoms_of_one_type in first_frame: + object_size_vec += [atom.location - object_center_vec for atom in atoms_of_one_type] + + object_size = 0.0 + object_size = max(object_size_vec).length + + # ------------------------------------------------------------------------ + # COLLECTION + + # Before we start to draw the atoms, we first create a collection for the + # atomic structure. All atoms (balls) are put into this collection. + coll_structure_name = os.path.basename(filepath_xyz) + scene = bpy.context.scene + coll_structure = bpy.data.collections.new(coll_structure_name) + scene.collection.children.link(coll_structure) + + # ------------------------------------------------------------------------ + # DRAWING THE ATOMS + + bpy.ops.object.select_all(action='DESELECT') + + # For each list of atoms of ONE type (e.g. Hydrogen) + for atoms_of_one_type in first_frame: + + # Create first the vertices composed of the coordinates of all + # atoms of one type + atom_vertices = [] + for atom in atoms_of_one_type: + # In fact, the object is created in the World's origin. + # This is why 'object_center_vec' is substracted. At the end + # the whole object is translated back to 'object_center_vec'. + atom_vertices.append( atom.location - object_center_vec ) + + # First, we create a collection of the element, which + # contains the atoms (balls + mesh)! + coll_element_name = atom.name # the element name + # Create the new collection and ... + coll_element = bpy.data.collections.new(coll_element_name) + # ... link it to the collection, which contains all parts of the + # structure. + coll_structure.children.link(coll_element) + + # Now, create a collection for the atoms, which includes the + # representative ball and the mesh. + coll_atom_name = atom.name + "_atom" + # Create the new collection and ... + coll_atom = bpy.data.collections.new(coll_atom_name) + # ... link it to the collection, which contains all parts of the + # element (ball and mesh). + coll_element.children.link(coll_atom) + + # Build the mesh + atom_mesh = bpy.data.meshes.new("Mesh_"+atom.name) + atom_mesh.from_pydata(atom_vertices, [], []) + atom_mesh.update() + new_atom_mesh = bpy.data.objects.new(atom.name + "_mesh", atom_mesh) + + # Link active object to the new collection + coll_atom.objects.link(new_atom_mesh) + + # Now, build a representative sphere (atom) + if atom.name == "Vacancy": + bpy.ops.mesh.primitive_cube_add( + view_align=False, enter_editmode=False, + location=(0.0, 0.0, 0.0), + rotation=(0.0, 0.0, 0.0)) + else: + # NURBS balls + if Ball_type == "0": + bpy.ops.surface.primitive_nurbs_surface_sphere_add( + view_align=False, enter_editmode=False, + location=(0,0,0), rotation=(0.0, 0.0, 0.0)) + # UV balls + elif Ball_type == "1": + bpy.ops.mesh.primitive_uv_sphere_add( + segments=Ball_azimuth, ring_count=Ball_zenith, + size=1, view_align=False, enter_editmode=False, + location=(0,0,0), rotation=(0, 0, 0)) + # Meta balls + elif Ball_type == "2": + bpy.ops.object.metaball_add(type='BALL', view_align=False, + enter_editmode=False, location=(0, 0, 0), + rotation=(0, 0, 0)) + + ball = bpy.context.view_layer.objects.active + # Hide this ball because its appearance has no meaning. It is just the + # representative ball. The ball is visible at the vertices of the mesh. + # Rememmber, this is a dupliverts construct! + ball.hide_set(True) + # Scale up/down the ball radius. + ball.scale = (atom.radius*Ball_radius_factor,) * 3 + + if atom.name == "Vacancy": + ball.name = atom.name + "_cube" + else: + ball.name = atom.name + "_ball" + ball.active_material = atom.material + ball.parent = new_atom_mesh + new_atom_mesh.instance_type = 'VERTS' + # The object is back translated to 'object_center_vec'. + new_atom_mesh.location = object_center_vec + STRUCTURE.append(new_atom_mesh) + + # Note the collection where the ball was placed into. + coll_all = ball.users_collection + if len(coll_all) > 0: + coll_past = coll_all[0] + else: + coll_past = bpy.context.scene.collection + + # Put the atom into the new collection 'atom' and ... + coll_atom.objects.link(ball) + # ... unlink the atom from the other collection. + coll_past.objects.unlink(ball) + + # ------------------------------------------------------------------------ + # CAMERA and LIGHT SOURCES + + camera_light_source(use_camera, + use_light, + object_center_vec, + object_size) + + # ------------------------------------------------------------------------ + # SELECT ALL LOADED OBJECTS + + bpy.ops.object.select_all(action='DESELECT') + obj = None + for obj in STRUCTURE: + obj.select_set(True) + # activate the last selected object (perhaps another should be active?) + if obj: + bpy.context.view_layer.objects.active = obj + + + +def build_frames(frame_delta, frame_skip): + + scn = bpy.context.scene + + # Introduce the basis for all elements that appear in the structure. + for element in STRUCTURE: + + bpy.ops.object.select_all(action='DESELECT') + bpy.context.view_layer.objects.active = element + element.select_set(True) + bpy.ops.object.shape_key_add(True) + + frame_skip += 1 + + # Introduce the keys and reference the atom positions for each key. + i = 0 + for j, frame in enumerate(ALL_FRAMES): + + if j % frame_skip == 0: + + for elements_frame, elements_structure in zip(frame,STRUCTURE): + + key = elements_structure.shape_key_add() + + for atom_frame, atom_structure in zip(elements_frame, key.data): + + atom_structure.co = (atom_frame.location + - elements_structure.location) + + key.name = atom_frame.name + "_frame_" + str(i) + + i += 1 + + num_frames = i + + scn.frame_start = 0 + scn.frame_end = frame_delta * num_frames + + # Manage the values of the keys + for element in STRUCTURE: + + scn.frame_current = 0 + + element.data.shape_keys.key_blocks[1].value = 1.0 + element.data.shape_keys.key_blocks[2].value = 0.0 + element.data.shape_keys.key_blocks[1].keyframe_insert("value") + element.data.shape_keys.key_blocks[2].keyframe_insert("value") + + scn.frame_current += frame_delta + + number = 0 + + for number in range(num_frames)[2:]:#-1]: + + element.data.shape_keys.key_blocks[number-1].value = 0.0 + element.data.shape_keys.key_blocks[number].value = 1.0 + element.data.shape_keys.key_blocks[number+1].value = 0.0 + element.data.shape_keys.key_blocks[number-1].keyframe_insert("value") + element.data.shape_keys.key_blocks[number].keyframe_insert("value") + element.data.shape_keys.key_blocks[number+1].keyframe_insert("value") + + scn.frame_current += frame_delta + + number += 1 + + element.data.shape_keys.key_blocks[number].value = 1.0 + element.data.shape_keys.key_blocks[number-1].value = 0.0 + element.data.shape_keys.key_blocks[number].keyframe_insert("value") + element.data.shape_keys.key_blocks[number-1].keyframe_insert("value") -- cgit v1.2.3