def plane(coords, findBounds=False): """Compute a plane given an Nx3 Numpy array. Returns a Plane whose origin is the centroid of the coords. If 'findBounds' is True, returns an additional Point (the furthest point projected into plane) and an additional floating-point number (distance from origin to that point). Tip: you generally generate the input Numpy array with numpyArrayFromAtoms() """ import numpy from numpy.linalg import eig, svd, eigh centroid = coords.mean(0) centered = coords - centroid ignore, vals, vecs = svd(centered) normal = vecs[numpy.argmin(vals)] from chimera import Point, Plane, Vector origin = Point(*centroid) plane = Plane(origin, Vector(*normal)) if findBounds: maxSqDist = None for coord in coords: projected = plane.nearest(Point(*coord)) sqDist = origin.sqdistance(projected) if maxSqDist == None or sqDist > maxSqDist: maxSqDist = sqDist furthest = projected from math import sqrt return plane, projected, sqrt(maxSqDist) return plane
def pointDistances(self, target): if isinstance(target, chimera.Point): points = [target] else: points = target from chimera import cross, Plane dists = [] minExt = min(self.extents) maxExt = max(self.extents) xfCenter = self.xformCenter() xfDirection = self.xformDirection() minPt = xfCenter + xfDirection * minExt maxPt = xfCenter + xfDirection * maxExt for pt in points: v = pt - xfCenter c1 = cross(v, xfDirection) if c1.length == 0.0: # colinear inPlane = pt else: plane = Plane(xfCenter, cross(c1, xfDirection)) inPlane = plane.nearest(pt) ptExt = (inPlane - xfCenter) * xfDirection if ptExt < minExt: measurePt = minPt elif ptExt > maxExt: measurePt = maxPt else: measurePt = inPlane dists.append(pt.distance(measurePt)) return dists
def _axisDistance(self, axis, infinite=False): from chimera import angle, cross, Plane # shortest distance between lines is perpendicular to both... sDir = self.xformDirection() aDir = axis.xformDirection() if angle(sDir, aDir) in [0.0, 180.0]: # parallel return self._axisEndsDist(axis) shortDir = cross(sDir, aDir) # can use analytically shortest dist only if each axis # penetrates the plane formed by the other axis and the # perpendicular if not infinite: for a1, a2 in [(axis, self), (self, axis)]: normal = cross(a1.xformDirection(), shortDir) plane = Plane(a1.xformCenter(), normal) d1 = plane.distance(a2.xformCenter() + a2.xformDirection() * a2.extents[0]) d2 = plane.distance(a2.xformCenter() + a2.xformDirection() * a2.extents[1]) if cmp(d1, 0.0) == cmp(d2, 0.0): # both ends on same side of plane return self._axisEndsDist(axis) # D is perpendicular distance to origin d1 = Plane(self.xformCenter(), shortDir).equation()[3] d2 = Plane(axis.xformCenter(), shortDir).equation()[3] return abs(d1 - d2)
def clip_model(m, axis, origin, offset, slab, thickness, flip): if origin is None: origin = m.clipPlane.origin.data() if axis is None: axis = (-m.clipPlane.normal).data() if flip: axis = tuple([-x for x in axis]) if offset != 0: origin = apply_offset(origin, axis, offset) from chimera import Plane, Point, Vector p = Plane() p.origin = Point(*origin) p.normal = -Vector(*axis) m.clipPlane = p if not slab is None: m.useClipThickness = slab if not thickness is None: m.clipThickness = thickness m.useClipPlane = True
def tetraPos(bondee, bonded, bondLen, toward=None, away=None, toward2=None, away2=None): newBonded = [] curBonded = bonded[:] if len(curBonded) == 0: pos = singlePos(bondee, bondLen, toward, away) toward = toward2 away = away2 newBonded.append(pos) curBonded.append(pos) if len(curBonded) == 1: # add at 109.5 degree angle coplanar = toward or away if coplanar: coplanar = [coplanar] else: coplanar = None pos = anglePos(bondee, curBonded[0], bondLen, 109.5, coplanar=coplanar) if toward or away: # find the other 109.5 position in the toward/away # plane and the closer/farther position as appropriate old = bondee - curBonded[0] old.normalize() new = pos - bondee midpoint = bondee + old * new.length * cos705 otherPos = pos + (midpoint - pos) * 2 d1 = (pos - (toward or away)).sqlength() d2 = (otherPos - (toward or away)).sqlength() if toward: if d2 < d1: pos = otherPos elif away and d2 > d1: pos = otherPos newBonded.append(pos) curBonded.append(pos) if len(curBonded) == 2: # add along anti-bisector of current bonds and raised up # 54.75 degrees from plane of those bonds (half of 109.5) v1 = curBonded[0] - bondee v2 = curBonded[1] - bondee v1.normalize() v2.normalize() antiBi = v1 + v2 antiBi.negate() antiBi.normalize() # in order to stabilize the third and fourth tetrahedral # positions, cross the longer vector by the shorter if v1.sqlength() > v2.sqlength(): crossV = cross(v1, v2) else: crossV = cross(v2, v1) crossV.normalize() antiBi = antiBi * cos5475 * bondLen crossV = crossV * sin5475 * bondLen pos = bondee + antiBi + crossV if toward or away: otherPos = bondee + antiBi - crossV d1 = (pos - (toward or away)).sqlength() d2 = (otherPos - (toward or away)).sqlength() if toward: if d2 < d1: pos = otherPos elif away and d2 > d1: pos = otherPos newBonded.append(pos) curBonded.append(pos) if len(curBonded) == 3: unitized = [] for cb in curBonded: v = cb - bondee v.normalize() unitized.append(bondee + v) pl = Plane(unitized) norm = pl.normal # if normal on other side of plane from bondee, we need to # invert the normal; the (signed) distance from bondee # to the plane indicates if it is on the same side # (positive == same side) d = pl.distance(bondee) if d < 0.0: norm.negate() newBonded.append(bondee + norm * bondLen) return newBonded
import chimera from chimera import Plane, Xform, cross, Vector, Point m = chimera.openModels.list()[0] b = chimera.selection.currentBonds()[0] bondVec = b.atoms[0].coord() - b.atoms[1].coord() bondVec.normalize() axis = Vector(1.0, 0.0, 0.0) crossProd = cross(axis, bondVec) if crossProd.sqlength() > 0: from math import acos, degrees xform = Xform.rotation(crossProd, degrees(acos(axis * bondVec))) xform.invert() else: xform = Xform.identity() m.openState.xform = xform # okay, that puts the bond parallel to the X axis, now swing the plane of # the rest of the molecule into the xy plane... molPlane = Plane([a.xformCoord() for a in m.atoms[:3]]) angle = chimera.angle(molPlane.normal, Vector(0, 0, -1)) xform2 = Xform.rotation(b.atoms[0].xformCoord() - b.atoms[1].xformCoord(), angle) xform2.multiply(xform) m.openState.xform = xform2