Added XYZ animation frames #1
@ -8,12 +8,12 @@
|
|||||||
# Start of project : 2011-08-31 by CB
|
# Start of project : 2011-08-31 by CB
|
||||||
# First publication in Blender : 2011-11-11 by CB
|
# First publication in Blender : 2011-11-11 by CB
|
||||||
# Fusion of the PDB, XYZ and Panel : 2019-03-22 by CB
|
# Fusion of the PDB, XYZ and Panel : 2019-03-22 by CB
|
||||||
# Last modified : 2024-05-28 by CB
|
# Last modified : 2024-07-27 by JF
|
||||||
#
|
#
|
||||||
# Contributing authors
|
# Contributing authors
|
||||||
# ====================
|
# ====================
|
||||||
|
# John Ferrier (ferrier.j@northeastern.edu)
|
||||||
#
|
#
|
||||||
# So far ... none ... .
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Acknowledgements
|
# Acknowledgements
|
||||||
|
@ -27,7 +27,6 @@ from .xyz_export import export_xyz
|
|||||||
class IMPORT_OT_xyz(Operator, ImportHelper):
|
class IMPORT_OT_xyz(Operator, ImportHelper):
|
||||||
bl_idname = "import_mesh.xyz"
|
bl_idname = "import_mesh.xyz"
|
||||||
bl_label = "Import XYZ (*.xyz)"
|
bl_label = "Import XYZ (*.xyz)"
|
||||||
bl_description = "Import a XYZ atomic structure"
|
|
||||||
bl_options = {'PRESET', 'UNDO'}
|
bl_options = {'PRESET', 'UNDO'}
|
||||||
|
|
||||||
filename_ext = ".xyz"
|
filename_ext = ".xyz"
|
||||||
@ -162,7 +161,7 @@ class IMPORT_OT_xyz(Operator, ImportHelper):
|
|||||||
self.use_center_all,
|
self.use_center_all,
|
||||||
self.use_camera,
|
self.use_camera,
|
||||||
self.use_lamp,
|
self.use_lamp,
|
||||||
filepath_xyz)
|
filepath_xyz, self.use_frames) # Added self.use_frames for animation purposes in xyz_import.py [ read_xyz_file() ]
|
||||||
|
|
||||||
# Load frames
|
# Load frames
|
||||||
if len(ALL_FRAMES) > 1 and self.use_frames:
|
if len(ALL_FRAMES) > 1 and self.use_frames:
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import bpy
|
import bpy
|
||||||
|
import re
|
||||||
from math import pi, sqrt
|
from math import pi, sqrt
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
|
|
||||||
@ -192,144 +193,176 @@ def read_elements():
|
|||||||
ELEMENTS.append(li)
|
ELEMENTS.append(li)
|
||||||
|
|
||||||
|
|
||||||
|
def extract_number(file_name):
|
||||||
|
match = re.findall(r'\d+', file_name)
|
||||||
|
return int(''.join(match)) if match else 0
|
||||||
|
|
||||||
|
|
||||||
# filepath_pdb: path to pdb file
|
# filepath_pdb: path to pdb file
|
||||||
# radiustype : '0' default
|
# radiustype : '0' default
|
||||||
# '1' atomic radii
|
# '1' atomic radii
|
||||||
# '2' van der Waals
|
# '2' van der Waals
|
||||||
def read_xyz_file(filepath_xyz,radiustype):
|
def read_xyz_file(filepath_xyz,radiustype, use_frames):
|
||||||
|
|
||||||
number_frames = 0
|
number_frames = 0
|
||||||
total_number_atoms = 0
|
total_number_atoms = 0
|
||||||
|
|
||||||
# Open the file ...
|
#### Added .xyz sequence for animations! Original author has animation functions built in but
|
||||||
filepath_xyz_p = open(filepath_xyz, "r")
|
# never allowed for ALL_FRAMES to be built out. Just added a functionality for ALL_FRAMES.
|
||||||
|
# Files need to be in the format of my_cool_file_1.xyz, my_cool_file_2.xyz, ..., my_cool_file_x.xyz
|
||||||
|
# No other numbers, as extract_number() puts all numbers together for sorting. Guess it could work if
|
||||||
|
# the pre-emptive numbers are all the same... This was mostly just a quick fix for my use-case though.
|
||||||
|
# - JF
|
||||||
|
|
||||||
#Go through the whole file.
|
# Get the file directory
|
||||||
FLAG = False
|
file_dir = os.path.dirname( filepath_xyz )
|
||||||
for line in filepath_xyz_p:
|
files = []
|
||||||
|
|
||||||
# ... the loop is broken here (EOF) ...
|
# If the use_frames option is selected on import, create a full list of the .xyz files in the dir
|
||||||
if line == "":
|
if use_frames:
|
||||||
continue
|
for file in os.listdir( file_dir ):
|
||||||
|
if file.endswith('.xyz'):
|
||||||
|
files.append( file )
|
||||||
|
|
||||||
split_list = line.rsplit()
|
# Sort the list of .xyz files
|
||||||
|
files = sorted( files, key = extract_number )
|
||||||
|
|
||||||
if len(split_list) == 1:
|
else:
|
||||||
number_atoms = int(split_list[0])
|
# If not, just append the single file
|
||||||
FLAG = True
|
files.append( filepath_xyz )
|
||||||
|
|
||||||
if FLAG == True:
|
# Cycle through each sorted file
|
||||||
|
for xyzf in files:
|
||||||
|
|
||||||
line = filepath_xyz_p.readline()
|
# Open the file ...
|
||||||
line = line.rstrip()
|
filepath_xyz_p = open( os.path.join( file_dir, xyzf ), "r")
|
||||||
|
|
||||||
all_atoms= []
|
#Go through the whole file.
|
||||||
for i in range(number_atoms):
|
FLAG = False
|
||||||
|
for line in filepath_xyz_p:
|
||||||
|
|
||||||
|
# ... the loop is broken here (EOF) ...
|
||||||
|
if line == "":
|
||||||
|
continue
|
||||||
|
|
||||||
# This is a guarantee that only the total number of atoms of the
|
split_list = line.rsplit()
|
||||||
# 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
|
|
||||||
|
|
||||||
|
if len(split_list) == 1:
|
||||||
|
number_atoms = int(split_list[0])
|
||||||
|
FLAG = True
|
||||||
|
|
||||||
|
if FLAG == True:
|
||||||
|
|
||||||
line = filepath_xyz_p.readline()
|
line = filepath_xyz_p.readline()
|
||||||
line = line.rstrip()
|
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.
|
all_atoms= []
|
||||||
FLAG_FOUND = False
|
for i in range(number_atoms):
|
||||||
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 = []
|
# This is a guarantee that only the total number of atoms of the
|
||||||
for atom in all_atoms:
|
# 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
|
FLAG_FOUND = False
|
||||||
for element in elements:
|
for element in ELEMENTS:
|
||||||
# If the atom name is already in the list,
|
if str.upper(short_name) == str.upper(element.short_name):
|
||||||
# FLAG on 'True'.
|
# Give the atom its proper name, color and radius:
|
||||||
if element == atom[1]:
|
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
|
FLAG_FOUND = True
|
||||||
break
|
break
|
||||||
# No name in the current list has been found? => New entry.
|
|
||||||
|
# Is it a vacancy or an 'unknown atom' ?
|
||||||
if FLAG_FOUND == False:
|
if FLAG_FOUND == False:
|
||||||
# Stored are: Atom label (e.g. 'Na'), the corresponding
|
# Give this atom also a name. If it is an 'X' then it is a
|
||||||
# atom name (e.g. 'Sodium') and its color.
|
# vacancy. Otherwise ...
|
||||||
elements.append(atom[1])
|
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
|
||||||
|
|
||||||
# Sort the atoms: create lists of atoms of one type
|
x = float(split_list[1])
|
||||||
structure = []
|
y = float(split_list[2])
|
||||||
for element in elements:
|
z = float(split_list[3])
|
||||||
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)
|
location = Vector((x,y,z))
|
||||||
number_frames += 1
|
|
||||||
FLAG = False
|
|
||||||
|
|
||||||
filepath_xyz_p.close()
|
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
|
return total_number_atoms
|
||||||
|
|
||||||
@ -441,7 +474,7 @@ def import_xyz(Ball_type,
|
|||||||
put_to_center_all,
|
put_to_center_all,
|
||||||
use_camera,
|
use_camera,
|
||||||
use_light,
|
use_light,
|
||||||
filepath_xyz):
|
filepath_xyz, use_frames):
|
||||||
|
|
||||||
# List of materials
|
# List of materials
|
||||||
atom_material_list = []
|
atom_material_list = []
|
||||||
@ -454,7 +487,7 @@ def import_xyz(Ball_type,
|
|||||||
# ------------------------------------------------------------------------
|
# ------------------------------------------------------------------------
|
||||||
# READING DATA OF ATOMS
|
# READING DATA OF ATOMS
|
||||||
|
|
||||||
Number_of_total_atoms = read_xyz_file(filepath_xyz, radiustype)
|
Number_of_total_atoms = read_xyz_file(filepath_xyz, radiustype, use_frames)
|
||||||
|
|
||||||
# We show the atoms of the first frame.
|
# We show the atoms of the first frame.
|
||||||
first_frame = ALL_FRAMES[0]
|
first_frame = ALL_FRAMES[0]
|
||||||
@ -540,7 +573,7 @@ def import_xyz(Ball_type,
|
|||||||
sum_vec = Vector((0.0,0.0,0.0))
|
sum_vec = Vector((0.0,0.0,0.0))
|
||||||
|
|
||||||
# Sum of all atom coordinates
|
# Sum of all atom coordinates
|
||||||
for (i, atoms_of_one_type) in enumerate(frame):
|
for i, atoms_of_one_type in enumerate(frame):
|
||||||
|
|
||||||
# This is a guarantee that only the total number of atoms of the
|
# 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
|
# first frame is used. Condition is, so far, that the number of
|
||||||
|
Loading…
Reference in New Issue
Block a user