Source code for schrodinger.graphics3d.polygon

"""
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)