Connect points with vectors#
Build a 3D molecular structure using points and vectors layer based on a fullerene C60 (60 carbon atoms molecule). See: https://en.wikipedia.org/wiki/Buckminsterfullerene
Atoms are displayed as spherically shaded points, where the bonds are drawn as vectors between atoms for whose the distance is less than a chosen cutoff value.
Atoms positions are provided while the bonds are derived directly from the coordinates.
If you’d like to read molecular structure files (PDB or MMCIF) in napari, see the napari-molecule-reader: https://napari-hub.org/plugins/napari-molecule-reader.html

/home/runner/work/docs/docs/.venv/lib/python3.12/site-packages/napari/_qt/qt_event_loop.py:49: UserWarning: System theme detection requires a Qt6 backend. Please switch to PyQt6 or PySide6 to use it.
theme_type=get_system_theme(),
/home/runner/work/docs/docs/.venv/lib/python3.12/site-packages/napari/_qt/qt_event_loop.py:49: UserWarning: System theme detection requires a Qt6 backend. Please switch to PyQt6 or PySide6 to use it.
theme_type=get_system_theme(),
import numpy as np
import napari
# C60 fullerene atomic coordinates in Angstroms
# N x D Numpy array for points layer
atoms = np.array([
[ 0.726656, -1.000157, 3.300459],
[ 1.175755, 0.382026, 3.300459],
[ 1.410183, -1.940951, 2.581756],
[ 2.281725, 0.741377, 2.581756],
[ 2.281725, 1.977639, 1.817704],
[ 0. , 1.236262, 3.300459],
[ 0. , 2.399147, 2.581756],
[ 1.175755, 2.781173, 1.817704],
[-0.683527, -2.941108, 1.817704],
[-1.410183, -1.940951, 2.581756],
[ 0.683527, -2.941108, 1.817704],
[-0.726656, -1.000157, 3.300459],
[-1.175755, 0.382026, 3.300459],
[-2.585938, -1.558925, 1.817704],
[-3.008381, -0.258779, 1.817704],
[-2.281725, 0.741377, 2.581756],
[ 0.726656, -3.399304, -0.581443],
[-0.726656, -3.399304, -0.581443],
[ 1.410183, -3.177213, 0.581443],
[-1.410183, -3.177213, 0.581443],
[-2.585938, -2.322977, 0.581443],
[-1.175755, -2.781173, -1.817704],
[-2.281725, -1.977639, -1.817704],
[-3.008381, -1.741534, -0.581443],
[ 3.008381, -1.741534, -0.581443],
[ 2.281725, -1.977639, -1.817704],
[ 2.585938, -2.322977, 0.581443],
[ 1.175755, -2.781173, -1.817704],
[ 0. , -2.399147, -2.581756],
[ 2.281725, -0.741377, -2.581756],
[ 1.175755, -0.382026, -3.300459],
[ 0. , -1.236262, -3.300459],
[ 3.008381, -0.258779, 1.817704],
[ 3.457479, 0.359351, 0.581443],
[ 2.585938, -1.558925, 1.817704],
[ 3.457479, -0.359351, -0.581443],
[ 3.008381, 0.258779, -1.817704],
[ 3.008381, 1.741534, 0.581443],
[ 2.585938, 2.322977, -0.581443],
[ 2.585938, 1.558925, -1.817704],
[-0.726656, 1.000157, -3.300459],
[ 0.726656, 1.000157, -3.300459],
[ 1.410183, 1.940951, -2.581756],
[-1.410183, 1.940951, -2.581756],
[-3.008381, 0.258779, -1.817704],
[-2.281725, -0.741377, -2.581756],
[-1.175755, -0.382026, -3.300459],
[-2.585938, 1.558925, -1.817704],
[-3.008381, 1.741534, 0.581443],
[-3.457479, 0.359351, 0.581443],
[-3.457479, -0.359351, -0.581443],
[-2.585938, 2.322977, -0.581443],
[-0.726656, 3.399304, 0.581443],
[-1.175755, 2.781173, 1.817704],
[-2.281725, 1.977639, 1.817704],
[-1.410183, 3.177213, -0.581443],
[ 0.683527, 2.941108, -1.817704],
[ 1.410183, 3.177213, -0.581443],
[ 0.726656, 3.399304, 0.581443],
[-0.683527, 2.941108, -1.817704]
])
viewer = napari.Viewer(ndisplay=3)
points_layer = viewer.add_points(atoms)
points_layer.shading = "spherical"
points_layer.face_color = "teal"
points_layer.border_width = 0
points_layer.size = 0.3
N = len(atoms)
r_cutoff = 1.3 * 1.7 # ~1.3 * C-C bonds length
bonds = []
# Process of creating bonds between atoms
# If the absolute distance between two atoms
# is less than r_cutoff then we append coordinates of the vector
# and its projection in every axis (x, y, z)
for i in range(N):
for j in range(i + 1, N):
rij = atoms[j] - atoms[i]
dist = np.linalg.norm(rij)
if dist <= r_cutoff:
bonds.append([atoms[i], rij])
# Finally, we pass N x 2 x D NumPy array to vectors layer
vectors_layer = viewer.add_vectors(np.array(bonds))
vectors_layer.edge_width = 0.1
vectors_layer.vector_style = "line"
vectors_layer.length = 1
vectors_layer.edge_color = "crimson"
viewer.dims.axis_labels = ("x", "y", "z")
viewer.floating_axes.visible = True
if __name__ == '__main__':
napari.run()