How to Correctly Add Perspective to a Gnuplot 3D connected Point Cloud Representing a Molecule?

醉酒当歌 提交于 2019-12-06 04:42:10

Heh. I'm a molecular graphics wonk myself, having written viewers and visualization tools since grad student days in the 1970s. And you know what? I really dislike the use of perspective in molecular graphics. So much so that I'll call the absence in gnuplot a feature rather than a limitation.

There is demo molecule.dem in the gnuplot collection showing simple molecular graphics. It works better in the development version of gnuplot (5.3), where you can use plotting style "with circles" rather than "with points" for the atoms. Here you go:

set title "GM1 pentasaccharide ball-and-stick representation"

set hidden3d
set border 0
unset tics
unset key
set title offset 0, screen -0.85
set view equal xyz
set view 348, 163, 1.64872, 1.14

set style fill transparent solid 0.9 border -1
atomcolor(name) = name[1:1] eq "O" ? 0xdd2222 : name [1:1] eq "N" ? 0x4444ff : 0x888888

splot 'GM1_sugar.pdb' using 6:7:8:(0.6):(atomcolor(strcol(3))) with circles fc rgb var, \
      'GM1_bonds.r3d' using 1:2:3:($5-$1):($6-$2):($7-$3) with vectors nohead lw 3 lc "black"

Notes:

  • The atom positions are read directly from a PDB file
  • Atom coloring is generated from the atom name (gray for carbon, blue for nitrogen, etc)
  • The bonds were generated from the same PDB file by the "bonds" utility in the Raster3D molecular graphics package
  • Occlusion of the atoms in back by the ones in front is handled by "set hidden3d"
  • Occlusion of the bonds is less satisfactory because the line segment is drawn all the way to the atom center whereas visually it would look better to terminate at the projected spherical surface of the atom.
  • Visual impression of depth is further helped by making the atoms partially transparent.

Some time ago, I tried something similar with gnuplot. Apparently, points and lines do not respect the 3D-order. However, it will work if you draw with surfaces, i.e. atoms=spheres and bonds=cylinders. The input is a list of atom positions and a bond list. You plot the spheres and cylinders at the atom and bond positions, respectively. For the bonds you have to apply some rotation matrix. Unfortunately, gnuplot does not support vector and matrix operations (at least I couldn't find it) that's why the code is a bit lengthy and confusing, but it seems to work. My guess is if the molecule has many more atoms the plotting might get a bit slow. I don't know where the black dots at the bottom of the spheres are coming from. The following code was tested under Win7/64, gnuplot 5.2.6 using a wxt terminal.

Code:

### draw a molecule
reset session

set view equal xyz
set view 45,45 
set border 4095
unset tics
unset colorbox
unset key
set margins 0,0,0,0

set style fill solid 1.0 noborder
set pm3d depthorder noborder
set pm3d lighting specular 0.5

set parametric
# sphere (atom prototype)
set isosamples 24
set samples 24
set urange [-pi/2:pi/2]
set vrange [0:2*pi]
Radius = 1
set table $Sphere
    splot Radius*cos(u)*cos(v), Radius*cos(u)*sin(v), Radius*sin(u)
unset table

# cylinder (bond prototype)
set isosamples 2
set samples 12
set urange [-pi:pi]
set vrange [0.2:0.8]
BondRadius = 0.05
set table $Cylinder
    splot BondRadius*cos(u), BondRadius*(sin(u)), v
unset table
unset parametric

# AtomNo., AtomType, X, Y, Z
$Atoms <<EOD
  1  6 -2.0625  0.0000  0.0000
  2  6 -1.6500 -0.7145  0.0000
  3  7 -0.8250 -0.7145  0.0000
  4  6 -0.4125  0.0000  0.0000
  5  6 -0.8250  0.7145  0.0000
  6  6 -1.6500  0.7145  0.0000
  7  6  0.4125  0.0000  0.0000
  8  7  0.8250 -0.7145  0.0000
  9  6  1.6500 -0.7145  0.0000
 10  6  2.0625  0.0000  0.0000
 11  6  1.6500  0.7145  0.0000
 12  6  0.8250  0.7145  0.0000
 13  1  2.0625  1.4289  0.0000
 14  1  0.4125  1.4289  0.0000
 15  1  2.8875  0.0000  0.0000
 16  1  2.0625 -1.4289  0.0000
 17  1 -0.4125  1.4289  0.0000
 18  1 -2.0625  1.4289  0.0000
 19  1 -2.8875  0.0000  0.0000
 20  1 -2.0625 -1.4289  0.0000
EOD

# Bond list 
# BondNo., Atom1, Atom2
$Bondlist <<EOD
 1  1  2
 2  2  3
 3  3  4
 4  4  5
 5  5  6
 6  6  1
 7  4  7
 8  7  8
 9  8  9
10  9 10
11 10 11
12 11 12
13 12  7
14 11 13
15 12 14
16 10 15
17  9 16
18  5 17
19  6 18
20  1 19
21  2 20
EOD

AtomType(atom) = int(word($Atoms[atom],2))
AtomName(n)  = n==1 ? "H" : n==6 ? "C" : n==7 ? "N" : n==8 ? "O" : "X"
AtomSizeScaling = 0.12
AtomSize(n)  = AtomSizeScaling*(n==1 ? 1.5 : n==6 ? 2.5 : n==7 ? 3.0 : n==8 ? 2.5 : 2.0)
AtomColor(n) = n==1 ? 1 : n==6 ? 6 : n==7 ? 7 : n==8 ? 8 : 0
Atom(atom,axis) = word($Atoms[atom],axis+2) + (column(axis)*AtomSize(AtomType(atom)))
AtomPos(atom,axis) = word($Atoms[atom],axis+2)

set palette defined (-1 "#cccccc", 0 "#000000", 1 "#ffffff", 6 "#888888", 7 "#0000ff", 8 "#ff0000")

BondStart(i) = int(word($BondList[i],2))
BondEnd(i) = int(word($BondList[i],3))

# Bond direction vector
V_x(bond) = AtomPos(BondEnd(i),1) - AtomPos(BondStart(i),1) 
V_y(bond) = AtomPos(BondEnd(i),2) - AtomPos(BondStart(i),2)
V_z(bond) = AtomPos(BondEnd(i),3) - AtomPos(BondStart(i),3)

# rotation axis vector normalized, (cross-product of V and z-axis)
R_x(bond) =  V_y(bond)/sqrt(V_x(bond)**2 + V_y(bond)**2)
R_y(bond) = -V_x(bond)/sqrt(V_x(bond)**2 + V_y(bond)**2)
R_z(bond) = 0
# rotation angle (between V and z-axis)
A(bond) = -acos(V_z(bond)/sqrt(V_x(bond)**2+V_y(bond)**2+V_z(bond)**2))

# Rotation of points
P_x(bond,x,y,z) = x*(R_x(bond)*R_x(bond)*(1-cos(A(bond)))+          cos(A(bond))) + \
                  y*(R_x(bond)*R_y(bond)*(1-cos(A(bond)))-R_z(bond)*sin(A(bond))) + \
                  z*(R_x(bond)*R_z(bond)*(1-cos(A(bond)))+R_y(bond)*sin(A(bond))) + \
                  AtomPos(BondStart(i),1)

P_y(bond,x,y,z) = x*(R_y(bond)*R_x(bond)*(1-cos(A(bond)))+R_z(bond)*sin(A(bond))) + \
                  y*(R_y(bond)*R_y(bond)*(1-cos(A(bond)))+          cos(A(bond))) + \
                  z*(R_y(bond)*R_z(bond)*(1-cos(A(bond)))-R_x(bond)*sin(A(bond))) + \
                  AtomPos(BondStart(i),2)

P_z(bond,x,y,z) = x*(R_z(bond)*R_x(bond)*(1-cos(A(bond)))-R_y(bond)*sin(A(bond))) + \
                  y*(R_z(bond)*R_y(bond)*(1-cos(A(bond)))+R_x(bond)*sin(A(bond))) + \
                  z*(R_z(bond)*R_z(bond)*(1-cos(A(bond)))+          cos(A(bond))) + \
                  AtomPos(BondStart(i),3)

set cbrange [-1:8]
splot \
    for [i=1:|$BondList|] $Cylinder u \
    (P_x(i,$1,$2,$3)):(P_y(i,$1,$2,$3)):(P_z(i,$1,$2,$3)):(-1) w pm3d, \
    for [i=1:|$Atoms|] $Sphere u (Atom(i,1)):(Atom(i,2)):(Atom(i,3)):(AtomType(i)) w pm3d,\

### end of code

Result:

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!