"""
Module for drawing a tube in the Workspace.
Copyright Schrodinger, LLC. All rights reserved.
"""
import schrodinger
from . import arrow
from . import common
maestro = schrodinger.get_maestro()
[docs]class MaestroTube(common.Primitive):
""" A graphical tube (bond) drawn in the Workspace """
[docs] def __init__(self, point1, point2, color, radius, opacity=1.0):
"""
Create a Tube instance
:param list point1: The [X, Y, Z] points of the first tube end
:param list point2: The [X, Y, Z] points of the other tube end
:param tuple color: A tuple of (R, G, B) values between 0 and 1
:param float radius: The radius of the tube in ?Angstroms?
"""
self.points = [point1, point2]
self.color = color
self.radius = radius
self.opacity = opacity
self.calculateXYZBox()
self.createObject()
super().__init__([self.obj])
[docs] def calculateXYZBox(self):
"""
Pre-calculate some data used in the bounding box function
"""
self.xbox = sorted(a[0] for a in self.points)
self.ybox = sorted(a[1] for a in self.points)
self.zbox = sorted(a[2] for a in self.points)
[docs] def createObject(self):
"""
Create a graphics object
"""
self.obj = maestro.create_cylinder(*self.points[0],
*self.points[1],
*self.color,
self.radius,
self.opacity,
arrow.RESOLUTION_DEFAULT,
remove_endcaps=False)
def _calculateBoundingBox(self, mat):
"""
Determine the bounding box for the current workspace rotation
This method is required for all graphics primitives and is used to
determine the extent of the graphics when fitting the Workspace view to
its contents.
:param mat: workspace rotation matrix
:rtype: (list, list)
:return: Each list is 6 items long and specifies the bounding box (and
no, I don't undestand how)
"""
# Initialize lists
xyzmin = []
xyzmax = []
for k in range(6):
xyzmin.append(arrow.BOUNDING_BOX_INIT_VALUE)
xyzmax.append(-arrow.BOUNDING_BOX_INIT_VALUE)
# Determine the unrotated vertexes of an orthogonal box that completely
# contains the primitive. These are the 8 points that are formed by the
# minimum and maximum values of the X, Y and Z extents of the primative.
# Since this is a tube, the radius entends those values in the - and +
# directions as well. The points are (xmin - radius, ymin - radius,
# zmin-radius), (xmin - radius, ymax + radius, zmin - radius), i.e. all
# eight combinations of x, y and z min and max values.
pm_radius = (-self.radius, self.radius)
vertexes = []
for xval, rxval in zip(self.xbox, pm_radius):
for yval, ryval in zip(self.ybox, pm_radius):
for zval, rzval in zip(self.zbox, pm_radius):
vertexes.append((xval + rxval, yval + ryval, zval, rzval))
# This magic is stolen from the boundary box methods in the
# other schrodinger.graphics3d shape classes.
tmp = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
for i in range(8):
for k in range(3):
tmp[k + 3] = vertexes[i][k]
tmp[0] = mat[0][0] * tmp[3] + mat[0][1] * tmp[4] + mat[0][2] * tmp[
5] + mat[0][3]
tmp[1] = mat[1][0] * tmp[3] + mat[1][1] * tmp[4] + mat[1][2] * tmp[
5] + mat[1][3]
tmp[2] = mat[2][0] * tmp[3] + mat[2][1] * tmp[4] + mat[2][2] * tmp[
5] + mat[2][3]
for k in range(6):
if xyzmin[k] > tmp[k]:
xyzmin[k] = tmp[k]
if xyzmax[k] < tmp[k]:
xyzmax[k] = tmp[k]
return (xyzmin, xyzmax)
[docs] def setColor(self, color):
"""
Set the color of the object
:param tuple color: The (R, G, B) values in the range of 0-1
"""
self.color = color
maestro.set_colors(self.obj, *color, self.opacity)
[docs] def setRadius(self, radius):
"""
Set the radius of the object
:param float radius: The radius of the object
"""
self.radius = radius
maestro.set_radius(self.obj, radius)
[docs] def setPoints(self, point1, point2):
"""
Set the coordinates of the tube ends
:param list point1: The [X, Y, Z] points of the first tube end
:param list point2: The [X, Y, Z] points of the other tube end
"""
self.points = [point1, point2]
self.calculateXYZBox()
maestro.set_coords1(self.obj, *point1)
maestro.set_coords2(self.obj, *point2)
[docs] def setEntryID(self, eid):
"""
Set the entry id of the object. This is used for placing the workspace
graphics when the workspace is tiled.
:param str eid: The entry ID to associate the graphics with
"""
maestro.set_entry_id(self.obj, eid)