This is a read-only mirror of pymolwiki.org
Difference between revisions of "Bounding Box"
Jump to navigation
Jump to search
m (6 revisions) |
(No difference)
|
Latest revision as of 01:49, 28 March 2014
import math
from pymol import querying
from pymol.cgo import *
from pymol import cmd
#NOTE!! : These packages (numarray, Scientific) must be present in pymol's
#'$PYMOLDIR/py23/lib/python2.3/site-packages/' directory!!
# OR..if you are using Mac PyMOL 0.99beta19 or later then the system installs will be used
from numarray import *
from numarray.ma import average
from numarray import linear_algebra as la
from Scientific.Geometry import Vector, Tensor, Transformation
def boundingBox(selectionName, colourRGB=[1,1,1]):
"""
The main function to call :
run "box.py"
boundingBox("peptide")
Should make a box around "peptide" (assuming it exists!). For a different colour use:
boundingBox("peptide", colourRGB=[1, 0, 1])
Or whatever. The box should be a cgo called 'peptide-box' (for this example).
"""
model = querying.get_model(selectionName)
coords = model.get_coord_list()
#find the least square plane through the coordinates
eigenvalues, eigenvectors, centroid = findPlaneWithEigenVectors(coords)
normal = eigenvectors[eigenvalues.argmin()]
eigenvectors.remove(normal)
#orient the axes correctly
x, y, normal = orientAxes(eigenvectors[0], eigenvectors[1], normal)
#determine the dimensions and the structure's orientation wrt the origin
minDimensions, rotation = findMinDimensionsAndRotation(coords, centroid, x, y, normal)
#'create' the box(IE: make the corners) and 'draw' it (IE: make a cgo)
box = makeBox(minDimensions, rotation, centroid)
drawBox(box, selectionName, colourRGB)
def findPlaneWithEigenVectors(coords):
centroid = average(coords)
coords -= centroid
B = matrixmultiply(transpose(coords), coords)
eigenvalues, eigenvectors = la.eigenvectors(B)
#return eigenvalues, [Vector(e) for e in eigenvectors], Vector(centroid)
return eigenvalues, [Vector([i for i in e]) for e in eigenvectors], Vector(centroid) #not sure why I had to make this change!
def orientAxes(x, y, z):
XcrossY = x.cross(y)
#ZXY = around(math.degrees(z.angle(XcrossY)))
ZXY = int(around(math.degrees(z.angle(XcrossY)))) #again, a bit of a hack!
if (ZXY == 180): x, y = y, x
return x, y, z
def makeBox(dimensions, rotation, centroid):
x, y, z = dimensions
v = [[]] * 8
#make a cuboid with the lower corner on the origin
v[0] = [0, 0, 0] # [0, 0, 0]
v[1] = [x, 0, 0] # [1, 0, 0]
v[2] = [x, y, 0] # [1, 1, 0]
v[3] = [x, 0, z] # [1, 0, 1]
v[4] = [0, y, 0] # [0, 1, 0]
v[5] = [0, 0, z] # [0, 0, 1]
v[6] = [0, y, z] # [0, 1, 1]
v[7] = [x, y, z] # [1, 1, 1]
#move to the origin, THEN move to the centroid of the points, then rotate
translationToOrigin = Transformation.Translation(-Vector(x/2, y/2, z/2))
translationToCentroid = Transformation.Translation(centroid)
transform = translationToCentroid * rotation * translationToOrigin
#use the Transformation to transform the corners of the box
v = [transform(Vector(i)) for i in v]
bot = [v[0], v[1], v[2], v[4]] # O, x, xy, y
top = [v[7], v[3], v[5], v[6]] # xyz, xz, z, yz
minL = [v[0], v[4], v[6], v[5]] # O, y, yz, z
minR = [v[0], v[5], v[3], v[1]] # O, z, xz, x
maxL = [v[4], v[2], v[7], v[6]] # y, xy, xyz, yz
maxR = [v[3], v[1], v[2], v[7]] # xz, x, xy, xyz
box = [bot, minR, minL, maxR, maxL, top]
return box
def drawBox(box, name, colourRGB):
boxObj = []
for side in box:
boxObj.append(BEGIN)
boxObj.append(LINE_STRIP)
boxObj.append(COLOR)
boxObj.extend(colourRGB)
for point in side:
boxObj.append(VERTEX)
boxObj.extend(point)
boxObj.append(END)
cmd.set('auto_zoom', 0)
cmd.load_cgo(boxObj, "%s-box" % name)
cmd.set('auto_zoom', 1)
def findMinDimensionsAndRotation(coords, centroid, x, y, z):
O = Vector(0, 0, 0)
X = Vector(1, 0, 0)
Y = Vector(0, 1, 0)
Z = Vector(0, 0, 1)
#Create a Transformation t = |x, y, z| . |X, Y, Z| ^ -1
mfinal = array([array(X), array(Y), array(Z)])
morig = array([array(x), array(y), array(z)])
rotmatrix = matrixmultiply(morig, transpose(mfinal))
tTotal = Transformation.Rotation(Tensor(rotmatrix))
#Transform the coordinates and find the min, max dimensions
coordArray = array([array(tTotal(Vector(c))) for c in coords])
minDimensions = [max(coordArray[:,i]) - min(coordArray[:,i]) for i in range(3)]
#Now, compose the inverse rotation used to move the bounding box to the right place
tForward = Transformation.Rotation(Tensor(matrixmultiply(mfinal, transpose(morig))))
return minDimensions, tForward