"""
Maestro 3D polygons.
The polygon module allows creation and drawing of polygons. Clients draw using
Group instances not through Polygon instances.
Control over the vertices, color, and transparency of a polygon are provided.
See the Polygon class for more info.
To draw any number of polygons create the Polygon instances and add them to a
Group instance (PolygonGroup can be used and is a synonym). Then invoke the
Group's draw() method.
Copyright Schrodinger, LLC. All rights reserved.
"""
from schrodinger import get_maestro
from . import common
from .common import FILL
from .common import LINE
from .common import OPACITY_DEFAULT
from .common import OPACITY_MAX
from .common import OPACITY_MIN
from .common import Group
# NOTE: PolygonGroup is deprecated; use Group class.
PolygonGroup = Group
maestro = get_maestro()
# Constants used to calculate bounding box:
BOUNDING_BOX_INIT_VALUE = 100000000.0
[docs]class MaestroPolygon(common.Primitive):
[docs] def __init__(self,
vertices,
color=None,
opacity=OPACITY_DEFAULT,
style=FILL):
"""
Creates polygon object in Maestro.
Constructor requires:
vertices: List of vertex coordinates (x, y, z).
Specify at least 3 vertices in consecutive order.
All vertices must be in the same plane.
color: One of:
Color object (Color class)
Color name (string)
Tuple of (R, G, B) (each a float in range 0.0-1.0)
Optional arguments:
opacity: 0.0 (invisible) through 1.0 (opaque)
Defaults to 1.0
style: LINE or FILL. Default is FILL.
"""
self.vertices = []
if not vertices:
raise ValueError("Must specify vertices to define the polygon")
else:
self._checkVertexArgument(vertices)
# Assume each item is a list of 3 x, y, z ordinates
for i in range(len(vertices)):
self.vertices.append([float(item) for item in vertices[i]])
self.normal = common.create_normal(self.vertices)
if len(self.vertices) < 3:
raise ValueError("Must specify at least 3 vertices")
if color is None:
raise ValueError("Must specify a color")
self.r, self.g, self.b = common.color_arg_to_rgb(color)
# Clamp to range of 0.0 and 1.0, inclusive
opacity = float(opacity)
if opacity < OPACITY_MIN:
self.opacity = OPACITY_MIN
elif opacity > OPACITY_MAX:
self.opacity = OPACITY_MAX
else:
self.opacity = opacity
if (style != FILL and style != LINE):
raise ValueError(
"Must specify a valid style: FILL or LINE (see common.py)")
else:
self.style = style
self.polygon = maestro.create_polygon(vertices, self.r, self.g, self.b,
opacity)
maestro_objects = [self.polygon]
common.Primitive.__init__(self, maestro_objects)
def _checkVertexArgument(self, vertices):
"""
Private method to check if the vertices are in the coorect format
"""
errmsg = "Specify at least 3 vertices. You passed:\n" + \
("%s. \nPass in " % str(vertices)) + \
"a list of vertex lists: " + \
"[[x,y,z], [x,y,z], [x,y,z], ..., [x,y,z]]"
# Check to make sure we have vertices specified
# as n lists each of [x, y, z]
if not isinstance(vertices, list):
raise TypeError(errmsg)
elif len(vertices) < 3:
# Need at least 3 coordinates - 3 or more x,y,z lists
raise TypeError(errmsg)
elif isinstance(vertices[0], list) and len(vertices[0]) % 3 != 0:
# Every 3 makes a coordinate. We don't check every
# sub-list, just the first.
raise TypeError(errmsg)
return
def _calculateBoundingBox(self, mat):
xyzmin = []
xyzmax = []
for k in range(6):
xyzmin.append(BOUNDING_BOX_INIT_VALUE)
xyzmax.append(-BOUNDING_BOX_INIT_VALUE)
tmp = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
for vertex in self.vertices:
for k in range(3):
tmp[k + 3] = vertex[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)