예제 #1
0
def transform_square(xform, center):

    axis, angle_deg = xform.getRotation()
    trans = xform.getTranslation()
    t1 = trans - axis*(trans*axis)
    import chimera
    t2 = chimera.cross(axis, t1)

    from math import pi, sin, cos
    angle = angle_deg * pi / 180
    sa, ca = sin(angle), cos(angle)
    if 1-ca == 0:
        return None     # No rotation
    axis_offset = t1*.5 + t2*(.5*sa/(1-ca))
    c = chimera.Vector(center[0], center[1], center[2])
    cd = c - axis_offset
    sq1 = cd - axis*(cd*axis)
    sq2 = axis*sq1.length
    e = 2       # Factor for enlarging square
    dz = chimera.cross(axis, sq1) * .05 # Thickness vector
    corners = [p.data() for p in (c - sq1 - sq2*e - dz, c - sq1 + sq2*e - dz,
                                c + sq1*e + sq2*e - dz, c + sq1*e - sq2*e - dz,
                                c - sq1 - sq2*e + dz, c - sq1 + sq2*e + dz,
                                c + sq1*e + sq2*e + dz, c + sq1*e - sq2*e + dz,
                                )]
    return corners
예제 #2
0
def transform_square(xform, center):

    axis, angle_deg = xform.getRotation()
    trans = xform.getTranslation()
    t1 = trans - axis * (trans * axis)
    import chimera
    t2 = chimera.cross(axis, t1)

    from math import pi, sin, cos
    angle = angle_deg * pi / 180
    sa, ca = sin(angle), cos(angle)
    if 1 - ca == 0:
        return None  # No rotation
    axis_offset = t1 * .5 + t2 * (.5 * sa / (1 - ca))
    c = chimera.Vector(center[0], center[1], center[2])
    cd = c - axis_offset
    sq1 = cd - axis * (cd * axis)
    sq2 = axis * sq1.length
    e = 2  # Factor for enlarging square
    dz = chimera.cross(axis, sq1) * .05  # Thickness vector
    corners = [
        p.data() for p in (
            c - sq1 - sq2 * e - dz,
            c - sq1 + sq2 * e - dz,
            c + sq1 * e + sq2 * e - dz,
            c + sq1 * e - sq2 * e - dz,
            c - sq1 - sq2 * e + dz,
            c - sq1 + sq2 * e + dz,
            c + sq1 * e + sq2 * e + dz,
            c + sq1 * e - sq2 * e + dz,
        )
    ]
    return corners
예제 #3
0
 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
예제 #4
0
 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)
예제 #5
0
def rotate(molecule, at, alpha):
    if len(at) == 3:
        try:
            a1, a2, a3 = [a.coord() for a in at]
        except AttributeError:
            a1, a2, a3 = at
        axis_a = a1 - a2
        axis_b = a3 - a2
        delta = chimera.angle(a1, a2, a3) - alpha
        axis = chimera.cross(axis_a, axis_b)
        if axis.data() == (0.0, 0.0, 0.0):
            axis = chimera.cross(axis_a, axis_b + chimera.Vector(1, 0, 0))
            logger.warning("Had to choose arbitrary normal vector")
        pivot = a2
    elif len(at) == 4:
        try:
            a1, a2, a3, a4 = [a.coord() for a in at]
        except AttributeError:
            a1, a2, a3, a4 = at
        axis = a3 - a2
        delta = chimera.dihedral(a1, a2, a3, a4) - alpha
        pivot = a3
    else:
        raise ValueError(
            "Atom list must contain 3 (angle) or 4 (dihedral) atoms only")

    r = X.translation(pivot - ZERO)  # move to origin
    r.multiply(X.rotation(axis, -delta))  # rotate
    r.multiply(X.translation(ZERO - pivot))  # return to orig pos
    for a in molecule.atoms:
        a.setCoord(r.apply(a.coord()))
예제 #6
0
def getEndNormals(c0, c1, c2):
    v1 = c1 - c0
    v2 = c2 - c0
    binormal = chimera.cross(v1, v2)
    binormal.normalize()
    normal = chimera.cross(binormal, v1)
    normal.normalize()
    return normal, binormal
예제 #7
0
def getEndNormals(c0, c1, c2):
	v1 = c1 - c0
	v2 = c2 - c0
	binormal = chimera.cross(v1, v2)
	binormal.normalize()
	normal = chimera.cross(binormal, v1)
	normal.normalize()
	return normal, binormal
예제 #8
0
def getInteriorNormals(c0, cb, ca):
	vb = cb - c0
	va = ca - c0
	vab = ca - cb
	binormal = chimera.cross(va, vb)
	binormal.normalize()
	normal = chimera.cross(binormal, vab)
	normal.normalize()
	return normal, binormal
예제 #9
0
def getInteriorNormals(c0, cb, ca):
    vb = cb - c0
    va = ca - c0
    vab = ca - cb
    binormal = chimera.cross(va, vb)
    binormal.normalize()
    normal = chimera.cross(binormal, vab)
    normal.normalize()
    return normal, binormal
예제 #10
0
def minimizeTwist(d, nn, nb, on, ob):
    # To minimize twisting, we use the ribbon direction
    # (approximated by the vector from the previous residue
    # to the next residue) as the axis and project the normal
    # and binormal both for this (n) and previous (o) residue.
    # We don't care about maintaining handedness (defined by
    # direction, normal and binormal) or about orientation
    # (binormal and normal may be swapped).  So we find whether
    # which two vectors (one from this and one from previous
    # residue) match best.  That determines whether we match
    # normal to normal or normal to binormal.  Once we decide
    # that, we can flip normal and binormal so that they never
    # rotate more than 90 degrees.
    nn0 = chimera.cross(nn, d)
    nn0.normalize()
    on0 = chimera.cross(on, d)
    on0.normalize()
    nb0 = chimera.cross(nb, d)
    nb0.normalize()
    ob0 = chimera.cross(ob, d)
    ob0.normalize()
    ndotn = nn0 * on0
    bdotb = nb0 * ob0
    matchBest = max(abs(ndotn), abs(bdotb))
    ndotb = nn0 * ob0
    bdotn = nb0 * on0
    swapBest = max(abs(ndotb), abs(bdotn))
    if matchBest >= swapBest:
        # We match normal to normal, binormal to binormal
        if ndotn < 0:
            normal = -nn
        else:
            normal = nn
        if bdotb < 0:
            binormal = -nb
        else:
            binormal = nb
    else:
        # We match normal to binormal, binormal to normal
        if ndotb < 0:
            binormal = -nn
        else:
            binormal = nn
        if bdotn < 0:
            normal = -nb
        else:
            normal = nb
    return normal, binormal
예제 #11
0
def minimizeTwist(d, nn, nb, on, ob):
	# To minimize twisting, we use the ribbon direction
	# (approximated by the vector from the previous residue
	# to the next residue) as the axis and project the normal
	# and binormal both for this (n) and previous (o) residue.
	# We don't care about maintaining handedness (defined by
	# direction, normal and binormal) or about orientation
	# (binormal and normal may be swapped).  So we find whether
	# which two vectors (one from this and one from previous
	# residue) match best.  That determines whether we match
	# normal to normal or normal to binormal.  Once we decide
	# that, we can flip normal and binormal so that they never
	# rotate more than 90 degrees.
	nn0 = chimera.cross(nn, d)
	nn0.normalize()
	on0 = chimera.cross(on, d)
	on0.normalize()
	nb0 = chimera.cross(nb, d)
	nb0.normalize()
	ob0 = chimera.cross(ob, d)
	ob0.normalize()
	ndotn = nn0 * on0
	bdotb = nb0 * ob0
	matchBest = max(abs(ndotn), abs(bdotb))
	ndotb = nn0 * ob0
	bdotn = nb0 * on0
	swapBest = max(abs(ndotb), abs(bdotn))
	if matchBest >= swapBest:
		# We match normal to normal, binormal to binormal
		if ndotn < 0:
			normal = -nn
		else:
			normal = nn
		if bdotb < 0:
			binormal = -nb
		else:
			binormal = nb
	else:
		# We match normal to binormal, binormal to normal
		if ndotb < 0:
			binormal = -nn
		else:
			binormal = nn
		if bdotn < 0:
			normal = -nb
		else:
			normal = nb
	return normal, binormal
예제 #12
0
def interpolateCorkscrew(xf, c0, c1, f):
	"""Interpolate by splitting the transformation into a rotation
	and a translation along the axis of rotation."""
	import chimera, math
	dc = c1 - c0
	vr, a = xf.getRotation()		# a is in degrees
	tra = dc * vr				# magnitude of translation
						# along rotation axis
	vt = dc - tra * vr			# where c1 would end up if
						# only rotation is used
	cm = c0 + vt / 2
	v0 = chimera.cross(vr, vt)
	if v0.sqlength() <= 0.0:
		ident = chimera.Xform.identity()
		return ident, ident
	v0.normalize()
	if a != 0.0:
		l = vt.length / 2 / math.tan(math.radians(a / 2))
		cr = (cm + v0 * l).toVector()
	else:
		cr = chimera.Vector(0.0, 0.0, 0.0)

	Tinv = chimera.Xform.translation(-cr)
	R0 = chimera.Xform.rotation(vr, a * f)
	R1 = chimera.Xform.rotation(vr, -a * (1 - f))
	X0 = chimera.Xform.translation(cr + vr * (f * tra))
	X0.multiply(R0)
	X0.multiply(Tinv)
	X1 = chimera.Xform.translation(cr - vr * ((1 - f) * tra))
	X1.multiply(R1)
	X1.multiply(Tinv)
	return X0, X1
예제 #13
0
파일: mmcif.py 프로젝트: gregdp/mapq
def diha ( a1, a2, a3, a4 ) :
    #n1 = vnorm ( a1.coord(), a2.coord(), a3.coord() )
    #n2 = vnorm ( a2.coord(), a3.coord(), a4.coord() )
    #return numpy.arccos ( n2 * n1 * -1.0 ) * 180.0 / numpy.pi

    # http://math.stackexchange.com/questions/47059/how-do-i-calculate-a-dihedral-angle-given-cartesian-coordinates
    b1 = a2.coord() - a1.coord()
    b2 = a3.coord() - a2.coord()
    b3 = a4.coord() - a3.coord()

    n1 = chimera.cross ( b1, b2 ); n1.normalize()
    n2 = chimera.cross ( b2, b3 ); n2.normalize()
    m1 = chimera.cross ( n1, b2 ); m1.normalize()

    x = n1 * n2
    y = m1 * n2

    return -1.0 * numpy.arctan2 ( y, x) * 180.0 / numpy.pi
예제 #14
0
def _vmd_trans_angle(a, b, c, delta):
    """
    Simulates VMD's `trans angle` command
    """
    ZERO = Point(0, 0, 0)
    xf = chimera.Xform.translation(b - ZERO)
    xf.rotate(cross(a - b, b - c), delta)
    xf.translate(ZERO - b)
    return xf
예제 #15
0
def drawCylinder(radius, ep0, ep1, **kw):
	t = vrml.Transform()
	t.translation = chimera.Point([ep0, ep1]).data()
	delta = ep1 - ep0
	height = delta.length
	axis = chimera.cross(cylAxis, delta)
	cos = (cylAxis * delta) / height
	angle = math.acos(cos)
	t.rotation = (axis[0], axis[1], axis[2], angle)
	c = vrml.Cylinder(radius=radius, height=height, **kw)
	t.addChild(c)
	return t
예제 #16
0
def AlignXf ( pos, v ) :
    Z = v
    Z.normalize()
    dZ = Vector( random.random(), random.random(), random.random() )
    dZ.normalize()
    X = chimera.cross ( Z, dZ )
    X.normalize ()
    Y = chimera.cross ( Z, X )
    Y.normalize ()

    xf = chimera.Xform.xform (
        X.x, Y.x, Z.x, pos.x,
        X.y, Y.y, Z.y, pos.y,
        X.z, Y.z, Z.z, pos.z )

    #xf3 = chimera.Xform.xform (
    #    d, 0, 0, 0,
    #    0, d, 0, 0,
    #    0, 0, d, 0 )
    #print xf3

    return xf
예제 #17
0
파일: axes.py 프로젝트: gregdp/modelz
def AlignXf ( pos, v ) :
    Z = v
    Z.normalize()
    from random import random as rand
    dZ = chimera.Vector( rand(), rand(), rand() )
    dZ.normalize()
    X = chimera.cross ( Z, dZ )
    X.normalize ()
    Y = chimera.cross ( Z, X )
    Y.normalize ()

    xf = chimera.Xform.xform (
        X.x, Y.x, Z.x, pos[0],
        X.y, Y.y, Z.y, pos[1],
        X.z, Y.z, Z.z, pos[2] )

    #xf3 = chimera.Xform.xform (
    #    d, 0, 0, 0,
    #    0, d, 0, 0,
    #    0, 0, d, 0 )
    #print xf3

    return xf
예제 #18
0
def findPt(n1, n2, n3, dist, angle, dihed):
	# cribbed from Midas addgrp command
	v12 = n2 - n1
	v13 = n3 - n1
	v12.normalize()
	x = cross(v13, v12)
	x.normalize()
	y = cross(v12, x)
	y.normalize()

	mat = [0.0] * 12
	for i in range(3):
		mat[i*4] = x[i]
		mat[1 + i*4] = y[i]
		mat[2 + i*4] = v12[i]
		mat[3 + i*4] = n1[i]
	
	xform = Xform.xform(*mat)

	radAngle = pi * angle / 180.0
	tmp = dist * sin(radAngle)
	radDihed = pi * dihed / 180.0
	pt = Point(tmp*sin(radDihed), tmp*cos(radDihed), dist*cos(radAngle))
	return xform.apply(pt)
def findPt(n1, n2, n3, dist, angle, dihed):
    # cribbed from Midas addgrp command
    v12 = n2 - n1
    v13 = n3 - n1
    v12.normalize()
    x = cross(v13, v12)
    x.normalize()
    y = cross(v12, x)
    y.normalize()

    mat = [0.0] * 12
    for i in range(3):
        mat[i * 4] = x[i]
        mat[1 + i * 4] = y[i]
        mat[2 + i * 4] = v12[i]
        mat[3 + i * 4] = n1[i]

    xform = Xform.xform(*mat)

    radAngle = pi * angle / 180.0
    tmp = dist * sin(radAngle)
    radDihed = pi * dihed / 180.0
    pt = Point(tmp * sin(radDihed), tmp * cos(radDihed), dist * cos(radAngle))
    return xform.apply(pt)
def testPhi(dp, ap, bp, phiPlane, phi):
    if phiPlane:
        normal = cross(phiPlane[1] - phiPlane[0], phiPlane[2] - phiPlane[1])
        normal.normalize()
        D = normal * phiPlane[1].toVector()
        bproj = project(bp, normal, D)
        aproj = project(ap, normal, D)
        dproj = project(dp, normal, D)

        ang = angle(bproj, aproj, dproj)
        if ang < phi:
            if base.verbose:
                print "phi criteria failed (%g < %g)" % (ang, phi)
            return 0
        if base.verbose:
            print "phi criteria OK (%g >= %g)" % (ang, phi)
    else:
        if base.verbose:
            print "phi criteria irrelevant"
    return 1
예제 #21
0
def testPhi(dp, ap, bp, phiPlane, phi):
	if phiPlane:
		normal = cross(phiPlane[1] - phiPlane[0],
						phiPlane[2] - phiPlane[1])
		normal.normalize()
		D = normal * phiPlane[1].toVector()
		bproj = project(bp, normal, D)
		aproj = project(ap, normal, D)
		dproj = project(dp, normal, D)
		
		ang = angle(bproj, aproj, dproj)
		if ang < phi:
			if base.verbose:
				print "phi criteria failed (%g < %g)" % (ang, phi)
			return 0
		if base.verbose:
			print "phi criteria OK (%g >= %g)" % (ang, phi)
	else:
		if base.verbose:
			print "phi criteria irrelevant"
	return 1
예제 #22
0
def addDihedralBond(a1, a2, length, angleInfo, dihedInfo):
	"""Make bond between two models.

	   The models will be combined and the originals closed.
	   The new model will be opened in the same id/subid as the
	       non-moving model.

	   a1/a2 are atoms in different models.
	   a2 and atoms in its model will be moved to form the bond.
		'length' is the bond length.
		'angleInfo' is a two-tuple of sequence of three atoms and
			an angle that the three atoms should form.
		'dihedInfo' is like 'angleInfo', but 4 atoms.
		angleInfo/dihedInfo can be None if insufficient atoms
	"""

	if a1.molecule == a2.molecule:
		raise ValueError("Atoms to be bonded must be in different models")

	# first, get the distance correct
	from chimera import Xform, cross, angle, Point
	dvector = a1.xformCoord() - a2.xformCoord()
	dvector.length = dvector.length + length
	openState = a2.molecule.openState
	openState.globalXform(Xform.translation(dvector))

	# then angle
	if angleInfo:
		atoms, angleVal = angleInfo
		p1, p2, p3 = [a.xformCoord() for a in atoms]
		axis = cross(p1-p2, p2-p3)
		curAngle = angle(p1, p2, p3)
		delta = angleVal - curAngle
		v2 = p2 - Point(0.0, 0.0, 0.0)
		trans1 = Xform.translation(v2)
		v2.negate()
		trans2 = Xform.translation(v2)
		trans1.multiply(Xform.rotation(axis, delta))
		trans1.multiply(trans2)
		openState.globalXform(trans1)
예제 #23
0
def addDihedralBond(a1, a2, length, angleInfo, dihedInfo):
    """Make bond between two models.

	   The models will be combined and the originals closed.
	   The new model will be opened in the same id/subid as the
	       non-moving model.

	   a1/a2 are atoms in different models.
	   a2 and atoms in its model will be moved to form the bond.
		'length' is the bond length.
		'angleInfo' is a two-tuple of sequence of three atoms and
			an angle that the three atoms should form.
		'dihedInfo' is like 'angleInfo', but 4 atoms.
		angleInfo/dihedInfo can be None if insufficient atoms
	"""

    if a1.molecule == a2.molecule:
        raise ValueError("Atoms to be bonded must be in different models")

    # first, get the distance correct
    from chimera import Xform, cross, angle, Point
    dvector = a1.xformCoord() - a2.xformCoord()
    dvector.length = dvector.length + length
    openState = a2.molecule.openState
    openState.globalXform(Xform.translation(dvector))

    # then angle
    if angleInfo:
        atoms, angleVal = angleInfo
        p1, p2, p3 = [a.xformCoord() for a in atoms]
        axis = cross(p1 - p2, p2 - p3)
        curAngle = angle(p1, p2, p3)
        delta = angleVal - curAngle
        v2 = p2 - Point(0.0, 0.0, 0.0)
        trans1 = Xform.translation(v2)
        v2.negate()
        trans2 = Xform.translation(v2)
        trans1.multiply(Xform.rotation(axis, delta))
        trans1.multiply(trans2)
        openState.globalXform(trans1)
def rotate_around_axis(axis, origin, screen_xy, last_screen_xy):

    # Measure drag around axis with mouse on front clip plane.
    sx, sy = screen_xy
    from VolumeViewer import slice
    xyz, back_xyz = slice.clip_plane_points(sx, sy)
    lsx, lsy = last_screen_xy
    last_xyz, back_last_xyz = slice.clip_plane_points(lsx, lsy)

    a = apply(chimera.Vector, axis)
    a.normalize()
    o = apply(chimera.Vector, origin)
    v = apply(chimera.Vector, xyz)
    lv = apply(chimera.Vector, last_xyz)
    t = chimera.cross(a, v - o)
    t2 = t.sqlength()
    if t2 > 0:
        angle = (t * (v - lv)) / t2
    else:
        angle = 0

    import math
    angle_deg = 180 * angle / math.pi
    
    vo = apply(chimera.Vector, origin)
    va = apply(chimera.Vector, axis)

    xf = chimera.Xform()
    xf.translate(vo)
    xf.rotate(va, angle_deg)
    xf.translate(-vo)
    # Applying xf performs the two translations and rotations in the
    # opposite order of the function calls.  First it translates by -vo,
    # then rotates, then translates by +vo.
    
    move_active_models(xf)
예제 #25
0
    def _draw_star(self):
        center_att = Point(*self.center_att)
        center = Point(*self.center)
        p6 = Point(*self.p6)

        vec_AB = center_att - center
        shape_size = self.size * 1.5
        half_length = shape_size / 2.0
        thickness = shape_size / 4.0

        adjustment = half_length / center.distance(center_att)
        adj_vec_AB_1 = adjustment * vec_AB
        adj_vec_AB_2 = -1 * adj_vec_AB_1

        x1 = adj_vec_AB_1 + center
        x2 = adj_vec_AB_2 + center

        perp1 = normalize(cross(x1 - x2, x2 - p6)) * half_length
        perp2 = -1 * perp1

        o1 = perp1 + x1
        o2 = perp1 + x2
        o3 = perp2 + x1
        o4 = perp2 + x2
        d1 = x2 - center
        d2 = (x1 - center) * 0.5

        # Rotate by 72 degrees to identify other points of the star
        outer = [
            _rotate(o1, center, o2, alpha, d1) + center
            for alpha in (72, 144, 216, 288, 360)
        ]
        inner = [
            _rotate(o1, center, o2, alpha, d2) + center
            for alpha in (72, 144, 216, 288, 360)
        ]

        perp_for = thickness * normalize(cross(o1 - o2, o3 - o1))
        perp_back = -1 * perp_for
        center_1 = perp_for + center
        center_2 = perp_back + center

        color_and_points = {
            'outer_{}'.format(i + 1): value
            for i, value in enumerate(outer)
        }
        color_and_points.update(
            {'inner_{}'.format(i + 1): value
             for i, value in enumerate(inner)})
        color_and_points.update(
            dict(color1=self.color1,
                 color2=self.color2,
                 center_1=center_1,
                 center_2=center_2))

        bild = """
        .color {color1}
        .polygon {outer_1} {center_1} {inner_3}
        .polygon {outer_1} {inner_3} {center_2}
        .polygon {outer_1} {inner_4} {center_1}
        .polygon {outer_1} {center_2} {inner_4}
        .polygon {outer_2} {center_1} {inner_4}
        .polygon {outer_2} {inner_4} {center_2}
        .polygon {outer_2} {inner_5} {center_1}
        .polygon {outer_2} {center_2} {inner_5}
        .polygon {outer_3} {center_1} {inner_5}
        .polygon {outer_3} {inner_5} {center_2}
        .polygon {outer_3} {inner_1} {center_1}
        .polygon {outer_3} {center_2} {inner_1}
        .polygon {outer_4} {center_1} {inner_1}
        .polygon {outer_4} {inner_1} {center_2}
        .polygon {outer_4} {inner_2} {center_1}
        .polygon {outer_4} {center_2} {inner_2}
        .polygon {outer_5} {center_1} {inner_2}
        .polygon {outer_5} {inner_2} {center_2}
        .polygon {outer_5} {inner_3} {center_1}
        .polygon {outer_5} {center_2} {inner_3}
        """.format(**color_and_points)

        return self._build_vrml(bild)
예제 #26
0
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
예제 #27
0
			if pyrimidine_max[2] < max[2]:
				pyrimidine_max[2] = max[2]
pu = (purine_max[1] - purine_min[1])
py = (pyrimidine_max[1] - pyrimidine_min[1])
purine_pyrimidine_ratio = pu / (pu + py)
del b, coord, min, max, pu, py

# precompute z-plane rotation correction factor
zAxis = chimera.Vector(0, 0, 1)
for b in standard_bases.values():
	pts = [chimera.Point(*b[n]) for n in b["ring atom names"][0:2]]
	yAxis = pts[0] - pts[1]
	yAxis.normalize()
	yAxis.z = 0.0	# should be zero already
			# insurance, so yAxis is perpendicular to zAxis
	xAxis = chimera.cross(yAxis, zAxis)
	xf = chimera.Xform.xform(
		xAxis[0], yAxis[0], zAxis[0], 0.0,
		xAxis[1], yAxis[1], zAxis[1], 0.0,
		xAxis[2], yAxis[2], zAxis[2], 0.0,
		orthogonalize=True
	)
	# we can work in 2d because we know atoms are in z=0 plane
	#coords = [b[a][0:2] for a in atoms]
	#yAxis = [coords[0][0] - coords[1][0], coords[0][1] - coords[1][1]]
	#len = math.sqrt(yAxis[0] * yAxis[0] + yAxis[1] * yAxis[1])
	#yAxis[0] /= len
	#yAxis[1] /= len
	## x axis is perpendicular to y axis
	#xf = chimera.Xform.xform(
	#	yAxis[1], yAxis[0], 0.0, 0.0,
예제 #28
0
def drawSlab(residue, style, thickness, orient, shape, showGly):
	try:
		t = residue.type
		if t in ('PSU', 'P'):
			n = 'P'
		elif t in ('NOS', 'I'):
			n = 'I'
		else:
			n = nucleic3to1[t]
	except KeyError:
		return None
	standard = standard_bases[n]
	ring_atom_names = standard["ring atom names"]
	atoms = getRing(residue, ring_atom_names)
	if not atoms:
		return None
	plane = chimera.Plane([a.coord() for a in atoms])
	info = findStyle(style)
	type = standard['type']
	slab_corners = info[type]
	origin = residue.findAtom(anchor(info[ANCHOR], type)).coord()
	origin = plane.nearest(origin)

	pts = [plane.nearest(a.coord()) for a in atoms[0:2]]
	yAxis = pts[0] - pts[1]
	yAxis.normalize()
	xAxis = chimera.cross(yAxis, plane.normal)
	xf = chimera.Xform.xform(
		xAxis[0], yAxis[0], plane.normal[0], origin[0],
		xAxis[1], yAxis[1], plane.normal[1], origin[1],
		xAxis[2], yAxis[2], plane.normal[2], origin[2]
	)
	xf.multiply(standard["adjust"])

	color_kwds = _atom_color(atoms[0])

	na = vrml.Transform()
	na.translation = xf.getTranslation().data()
	axis, angle = xf.getRotation()
	na.rotation = (axis[0], axis[1], axis[2], math.radians(angle))
	#xf.invert()	# invert so xf maps residue space to standard space

	halfThickness = thickness / 2.0

	t = vrml.Transform()
	na.addChild(t)
	llx, lly = slab_corners[0]
	urx, ury = slab_corners[1]
	t.translation = (llx + urx) / 2.0, (lly + ury) / 2.0, 0
	if shape == 'box':
		b = vrml.Box(size=(urx - llx, ury - lly, 2.0 * halfThickness),
				**color_kwds)
	elif shape == 'tube':
		radius = (urx - llx) / 2
		t.scale = 1, 1, halfThickness / radius
		b = vrml.Cylinder(radius=radius, height=(ury - lly),
				**color_kwds)
	elif shape == 'ellipsoid':
		# need to reach anchor atom
		t.scale = ((urx - llx) / 20 * _SQRT2, (ury - lly) / 20 * _SQRT2,
							.1 * halfThickness)
		b = vrml.Sphere(radius=10, **color_kwds)
	t.addChild(b)

	if showGly:
		c1p = residue.findAtom("C1'")
		ba = residue.findAtom(anchor(info[ANCHOR], type))
		if c1p and ba:
			c1p.hide = False
			ba.hide = False

	if not orient:
		return na

	# show slab orientation by putting "bumps" on surface
	if standard['type'] == PYRIMIDINE:
		t = vrml.Transform()
		na.addChild(t)
		t.translation = (llx + urx) / 2.0, (lly + ury) / 2, halfThickness
		t.addChild(vrml.Sphere(radius=halfThickness, **color_kwds))
	else:
		# purine
		t = vrml.Transform()
		na.addChild(t)
		t.translation = (llx + urx) / 2.0, lly + (ury - lly) / 3, halfThickness
		t.addChild(vrml.Sphere(radius=halfThickness, **color_kwds))
		t = vrml.Transform()
		na.addChild(t)
		t.translation = (llx + urx) / 2.0, lly + (ury - lly) * 2 / 3, halfThickness
		t.addChild(vrml.Sphere(radius=halfThickness, **color_kwds))
	return na
예제 #29
0
    def _draw_cube(self):
        center = Point(*self.center)
        center_att = Point(*self.center_att)
        p6 = Point(*self.p6)
        vec_AB = center_att - center

        # Resize the shape. $size refers to the total size,
        # $half_length refers to the distance required create
        # points on either side of the geometric center.
        half_length = self.size / 2.0

        # Vec_AB is currently too large, we want it to be adjusted so that the distance is equal to the offset
        # that will be used to place the ponits around the geometric center. The equation asks the question,
        # what factor should be multiplied times the distance in order to prodce $half-length?
        adjustment = half_length / center.distance(center_att)

        # Adjust vector_AB by the amount determined in both the forward and reverse directions
        adj_vec_AB_1 = vec_AB * adjustment
        adj_vec_AB_2 = adj_vec_AB_1 * -1

        # Add two points along the line connecting the residues in the forward and reverse direction
        x1 = adj_vec_AB_1 + center
        x2 = adj_vec_AB_2 + center

        # perp_1 represents a point perpendicular to the previously created two
        perp_1 = normalize(cross(x1 - x2, x2 - p6))
        perp1 = perp_1 * half_length
        perp2 = perp1 * -1

        # Each 'o' represents a point on the box (o stands for original points, which are based on the two that
        # lie along the line connecting the two residues)
        o1 = perp1 + x1
        o2 = perp1 + x2
        o3 = perp2 + x1
        o4 = perp2 + x2

        # This creates 8 points (the corners of the box) based upon the coordinates of o1-o4
        perp_for = normalize(cross(o1 - o2, o3 - o1)) * half_length
        perp_back = perp_for * -1
        points = dict(s1=perp_for + o1,
                      s2=perp_for + o2,
                      s3=perp_for + o3,
                      s4=perp_for + o4,
                      s5=perp_back + o1,
                      s6=perp_back + o2,
                      s7=perp_back + o3,
                      s8=perp_back + o4)

        # Draw the Cube - some cubes require two colors, so the triangles are intentionally divided so that one
        # side shows both colors
        # Color 1 (blue, yellow, or green)
        bild = """
        .color {color1}
        .polygon {s2} {s3} {s4}
        .polygon {s1} {s2} {s6}
        .polygon {s4} {s7} {s8}
        .polygon {s5} {s6} {s8}
        .polygon {s2} {s4} {s8}
        .polygon {s1} {s5} {s7}
        .color {color2}
        .polygon {s1} {s3} {s2}
        .polygon {s3} {s7} {s4}
        .polygon {s1} {s6} {s5}
        .polygon {s5} {s8} {s7}
        .polygon {s2} {s8} {s6}
        .polygon {s1} {s7} {s3}
        """.format(color1=self.color1, color2=self.color2, **points)
        return self._build_vrml(bild)
예제 #30
0
def mult(a, b):
    return Quaternion(a.s * b.s - a.v * b.v,
                      b.v * a.s + a.v * b.s + chimera.cross(a.v, b.v))
예제 #31
0
    def _draw_diamond(self):
        center_att = Point(*self.center_att)
        center = Point(*self.center)
        p6 = Point(*self.p6)

        vec_AB = center_att - center
        shape_size = self.size * 0.5
        adjustment = shape_size / center.distance(center_att)
        adj_vec_AB_1 = adjustment * vec_AB
        adj_vec_AB_2 = -adjustment * vec_AB
        x1 = adj_vec_AB_1 + center
        x2 = adj_vec_AB_2 + center

        perp_1 = cross(x1 - x2, x2 - p6)
        perp1 = perp_1 * shape_size
        perp2 = perp_1 * -shape_size

        # The number of sides to the diamond is up for debate. It was suggested to use six sides since the
        # diamond could look like a cube that has been rotated; however, four seems to work since the cylinder
        # goes directly through one corner. Maybe it could be an option for the user?
        # Each 'o' represents a corner of a square that is centered on $geom_center
        o1 = perp1 + x1
        o2 = perp1 + x2
        o3 = perp2 + x1
        o4 = perp2 + x2

        # Determine coordinates for outer points of the diamond
        # Top point of star is $shape_size above geometric center
        # Vector distance between center and top point of star
        d1 = x2 - center
        outer = [
            _rotate(o1, center, o2, alpha, d1) + center
            for alpha in (90, 180, 270, 360)
        ]

        # The following function creates the top and bottom of the diamond by creating two points at the geometric
        # center that are perpendicular to the plane of the square (and parallel with the plane of the ring).
        perp_for = normalize(cross(o1 - o2, o3 - o1)) * shape_size
        top = perp_for + center
        bottom = perp_for * -1 + center

        color_and_points = {
            'outer_{}'.format(i + 1): value
            for i, value in enumerate(outer)
        }
        color_and_points.update(
            dict(top=top,
                 bottom=bottom,
                 color1=self.color1,
                 color2=self.color2))
        bild = """
        .color {color1}
        .polygon {outer_1} {bottom} {outer_2}
        .polygon {outer_1} {outer_4} {bottom}
        .polygon {outer_3} {top} {outer_2}
        .polygon {outer_3} {outer_4} {top}
        .color {color2}
        .polygon {outer_1} {outer_2} {top}
        .polygon {outer_1} {top} {outer_4}
        .polygon {outer_3} {outer_2} {bottom}
        .polygon {outer_3} {bottom} {outer_4}
        """.format(**color_and_points)
        return self._build_vrml(bild)
예제 #32
0
    def _draw_cone(self):
        center_att = Point(*self.center_att)
        center = Point(*self.center)
        p6 = Point(*self.p6)

        vec_AB = center_att - center
        half_length = self.size / 2.0

        # Vec_AB is currently too large, we want it to be adjusted so that the distance is equal to the offset
        # that will be used to place the points around the geometric center. The equation asks the question,
        # what factor should be multiplied times the distance in order to prodce $half-length?
        # Two adjustments are added to shift the geom_center of the shape.
        _adjustment = half_length / center.distance(center_att)
        adjustment1 = _adjustment * 0.66
        adjustment2 = _adjustment * 1.33
        # Adjust vector_AB by the amount determined in both the forward and reverse directions
        adj_vec_AB_1 = adjustment1 * vec_AB
        adj_vec_AB_2 = -adjustment2 * vec_AB

        # Add two points along the line connecting the residues in the forward and reverse direction
        x1 = adj_vec_AB_1 + center
        x2 = adj_vec_AB_2 + center

        # perp_1 represents a point perpendicular to the previously created two
        perp1 = normalize(cross(x1 - x2, x2 - p6))
        perp2 = -0.5 * perp1
        o1 = perp1 + x1
        o2 = perp2 + x1
        perp3 = normalize(cross(o1 - x2, x2 - center_att)) * half_length
        o3 = perp3 + x1

        # Determine coordinates for outer points of the rectangle
        # Top point of rectangle is $half_length above geometric center
        # Vector distance between center and top point of rectangle
        d1 = o3 - x1

        # Rotate by 90 degrees to identify other points of the rectangle - note that t5 is the same as x1
        outer = [
            _rotate(o1, o3, x1, alpha, d1) + x1
            for alpha in (45, 90, 135, 180, 225, 270, 315, 360)
        ]

        # Draw the cone
        color_and_points = {
            'outer_{}'.format(i + 1): value
            for i, value in enumerate(outer)
        }
        color_and_points.update(
            dict(x1=x1, x2=x2, color1=self.color1, color2=self.color2))

        bild = """
        .color {color1}
        .polygon {outer_1} {outer_2} {x1}
        .polygon {outer_1} {x1} {outer_8}
        .polygon {outer_3} {x1} {outer_2}
        .polygon {outer_3} {outer_4} {x1}
        .color {color2}
        .polygon {outer_5} {x1} {outer_4}
        .polygon {outer_5} {outer_6} {x1}
        .polygon {outer_7} {x1} {outer_6}
        .polygon {outer_7} {outer_8} {x1}
        .color {color1}
        .polygon {outer_1} {x2} {outer_2}
        .polygon {outer_1} {outer_8} {x2}
        .polygon {outer_5} {outer_4} {x2}
        .polygon {outer_5} {x2} {outer_6}
        .color {color2}
        .polygon {outer_3} {outer_2} {x2}
        .polygon {outer_3} {x2} {outer_4}
        .polygon {outer_7} {outer_6} {x2}
        .polygon {outer_7} {x2} {outer_8}
        """.format(**color_and_points)

        return self._build_vrml(bild)
예제 #33
0
    def _draw_rectangle(self):
        center_att = Point(*self.center_att)
        center = Point(*self.center)
        p6 = Point(*self.p6)

        vec_AB = center_att - center
        half_length = self.size / 1.2
        thickness = self.size / 1.8

        adjustment = half_length / center.distance(center_att)
        adj_vec_AB_1 = adjustment * vec_AB
        adj_vec_AB_2 = -adjustment * vec_AB

        x1 = adj_vec_AB_1 + center
        x2 = adj_vec_AB_2 + center

        perp1 = normalize(cross(x1 - x2, x2 - p6)) * half_length
        perp2 = perp1 * -1

        o1 = perp1 + x1
        o2 = perp1 + x2
        o3 = perp2 + x1
        o4 = perp2 + x2
        d1 = x2 - center

        outer = [
            _rotate(o1, center, o2, alpha, d1) + center
            for alpha in (45, 90, 225, 270)
        ]

        perp_for = thickness * normalize(cross(o1 - o2, o3 - o1))
        perp_back = perp_for * -1
        color_and_points = dict(center_1=perp_for + center,
                                center_2=perp_back + center,
                                front_1=perp_for + outer[0],
                                front_2=perp_for + outer[1],
                                front_3=perp_for + outer[2],
                                front_4=perp_for + outer[3],
                                back_1=perp_back + outer[0],
                                back_2=perp_back + outer[1],
                                back_3=perp_back + outer[2],
                                back_4=perp_back + outer[3],
                                color1=self.color1,
                                color2=self.color2)
        # Draw the rectangle

        bild = """
        .color {color1}
        .polygon {front_1} {front_2} {center_1}
        .polygon {front_1} {center_1} {front_4}
        .polygon {front_3} {center_1} {front_2}
        .polygon {front_3} {front_4} {center_1}
        .polygon {back_1} {center_2} {back_2}
        .polygon {back_1} {back_4} {center_2}
        .polygon {back_3} {back_2} {center_2}
        .polygon {back_3} {center_2} {back_4}
        .polygon {back_1} {back_2} {front_1}
        .polygon {back_2} {front_2} {front_1}
        .polygon {back_2} {back_3} {front_2}
        .polygon {back_3} {front_3} {front_2}
        .polygon {back_3} {back_4} {front_3}
        .polygon {back_4} {front_4} {front_3}
        .polygon {back_4} {back_1} {front_4}
        .polygon {back_1} {front_1} {front_4}
        """.format(**color_and_points)

        return self._build_vrml(bild)
예제 #34
0
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
예제 #35
0
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
예제 #36
0
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
예제 #37
0
    def _draw_hexagon(self):
        center_att = Point(*self.center_att)
        center = Point(*self.center)
        p6 = Point(*self.p6)

        vec_AB = center_att - center
        shape_size = self.size
        half_length = shape_size / 2.0
        thickness = shape_size / 4.0

        adjustment = half_length / center.distance(center_att)
        adj_vec_AB_1 = adjustment * vec_AB
        adj_vec_AB_2 = -1 * adj_vec_AB_1

        x1 = adj_vec_AB_1 + center
        x2 = adj_vec_AB_2 + center

        perp1 = normalize(cross(x1 - x2, x2 - p6)) * half_length
        perp2 = -1 * perp1

        o1 = perp1 + x1
        o2 = perp1 + x2
        o3 = perp2 + x1
        o4 = perp2 + x2
        d1 = x2 - center

        # Rotate by 60 degrees to identify other points of the hexagon - note that t5 is the same as n1
        outer = [
            _rotate(o1, center, o2, alpha, d1) + center
            for alpha in (0, 45, 135, 180, 225, 315)
        ]

        perp_for = thickness * normalize(cross(o1 - o2, o3 - o1))
        perp_back = -1 * perp_for

        color_and_points = dict(
            front_1=perp_for + outer[0],
            front_2=perp_for + outer[1],
            front_3=perp_for + outer[2],
            front_4=perp_for + outer[3],
            front_5=perp_for + outer[4],
            front_6=perp_for + outer[5],
            back_1=perp_back + outer[0],
            back_2=perp_back + outer[1],
            back_3=perp_back + outer[2],
            back_4=perp_back + outer[3],
            back_5=perp_back + outer[4],
            back_6=perp_back + outer[5],
            center_1=perp_for + center,
            center_2=perp_back + center,
            color1=self.color1,
            color2=self.color2,
        )

        bild = """
        .color {color1}
        .polygon {front_1} {front_2} {center_1}
        .polygon {front_1} {center_1} {front_6}
        .polygon {front_3} {center_1} {front_2}
        .polygon {front_3} {front_4} {center_1}
        .polygon {front_5} {center_1} {front_4}
        .polygon {front_5} {front_6} {center_1}
        .polygon {back_1} {center_2} {back_2}
        .polygon {back_1} {back_6} {center_2}
        .polygon {back_3} {back_2} {center_2}
        .polygon {back_3} {center_2} {back_4}
        .polygon {back_5} {back_4} {center_2}
        .polygon {back_5} {center_2} {back_6}
        .polygon {back_1} {back_2} {front_1}
        .polygon {back_2} {front_2} {front_1}
        .polygon {back_2} {back_3} {front_2}
        .polygon {back_3} {front_3} {front_2}
        .polygon {back_3} {back_4} {front_3}
        .polygon {back_4} {front_4} {front_3}
        .polygon {back_4} {back_5} {front_4}
        .polygon {back_5} {front_5} {front_4}
        .polygon {back_5} {back_6} {front_5}
        .polygon {back_6} {front_6} {front_5}
        .polygon {back_6} {back_1} {front_6}
        .polygon {back_1} {front_1} {front_6}
        """.format(**color_and_points)
        return self._build_vrml(bild)