|
|
|
"""
|
|
|
|
This module defines functions that need to be overwritten
|
|
|
|
in order for OpenGL to work with the main window. This
|
|
|
|
module is named the same as the actual widget in order
|
|
|
|
to make namespacing consistent.
|
|
|
|
|
|
|
|
To be clear, the actual widget is defined in the UI
|
|
|
|
generated code - `clusterview_ui.py`. The functions
|
|
|
|
here are imported as overrides to the OpenGL functions of
|
|
|
|
that widget.
|
|
|
|
"""
|
|
|
|
|
|
|
|
from enum import Enum
|
|
|
|
|
|
|
|
from OpenGL.GL import (glBegin, glClearColor, glColor4f, glEnable,
|
|
|
|
glEnd, GL_LIGHT0, GL_LIGHTING, GL_POINTS,
|
|
|
|
glPointSize, glVertex3f, glViewport)
|
|
|
|
|
|
|
|
from .exceptions import InvalidModeError, InvalidStateError
|
|
|
|
from .mode import Mode
|
|
|
|
from .points import PointSet
|
|
|
|
|
|
|
|
class Color(Enum):
|
|
|
|
BLUE = 0
|
|
|
|
|
|
|
|
# A simple map from Color -> RGBA 4-Tuple
|
|
|
|
# Note: The color values in the tuple are not RGB, but
|
|
|
|
# rather OpenGL percentage values for RGB.
|
|
|
|
COLOR_TO_RGBA = {
|
|
|
|
Color.BLUE: (0, 0.5, 1.0, 0.0)
|
|
|
|
}
|
|
|
|
|
|
|
|
# Constants set based on the size of the window.
|
|
|
|
__BOTTOM_LEFT = (0, 0)
|
|
|
|
__WIDTH = None
|
|
|
|
__HEIGHT = None
|
|
|
|
|
|
|
|
# Module-global state variables for our drawing
|
|
|
|
# state machine.
|
|
|
|
#
|
|
|
|
# Below functions have to mark these as `global` so
|
|
|
|
# the interpreter knows that the variables are not
|
|
|
|
# function local.
|
|
|
|
__current_mode = None
|
|
|
|
__current_event = None
|
|
|
|
__current_points = None
|
|
|
|
__current_context = None
|
|
|
|
|
|
|
|
|
|
|
|
def set_drawing_context(ctx):
|
|
|
|
"""
|
|
|
|
Sets the drawing context so that drawing functions can properly
|
|
|
|
interact with the widget.
|
|
|
|
"""
|
|
|
|
global __current_context
|
|
|
|
|
|
|
|
__current_context = ctx
|
|
|
|
|
|
|
|
def set_current_points(points):
|
|
|
|
"""
|
|
|
|
Sets the point state variable that will be passed in to the paint_gl
|
|
|
|
function, and further to the point painter functions in order to
|
|
|
|
render the scene.
|
|
|
|
|
|
|
|
Each time a point is added, removed, or edited this point set must be
|
|
|
|
updated.
|
|
|
|
|
|
|
|
@param points The PointSet representing the current scene.
|
|
|
|
"""
|
|
|
|
global __current_points
|
|
|
|
|
|
|
|
if not isinstance(points, PointSet):
|
|
|
|
raise ValueError("set_current_points must recieve a PointSet as its " +
|
|
|
|
"argument.")
|
|
|
|
|
|
|
|
__current_points = points
|
|
|
|
|
|
|
|
|
|
|
|
def set_drawing_mode(mode):
|
|
|
|
"""
|
|
|
|
State management function. It is useful to look at the
|
|
|
|
different drawing modes as modes in a state machine.
|
|
|
|
|
|
|
|
Calling this function when a mode changes allows the
|
|
|
|
OpenGL functions to take the correct drawing action
|
|
|
|
on the OpenGL Widget.
|
|
|
|
|
|
|
|
@param mode The current mode.
|
|
|
|
"""
|
|
|
|
global __current_context
|
|
|
|
global __current_mode
|
|
|
|
|
|
|
|
if __current_context is None:
|
|
|
|
raise InvalidStateError("Drawing context must be set before setting " +
|
|
|
|
"drawing mode")
|
|
|
|
|
|
|
|
if not isinstance(mode, Mode):
|
|
|
|
raise ValueError("Mode in set_drawing_mode must be of type Mode")
|
|
|
|
|
|
|
|
__current_mode = mode
|
|
|
|
|
|
|
|
|
|
|
|
def set_drawing_event(event):
|
|
|
|
"""
|
|
|
|
State machine event management function.
|
|
|
|
|
|
|
|
@param event The event.
|
|
|
|
"""
|
|
|
|
global __current_context
|
|
|
|
global __current_event
|
|
|
|
|
|
|
|
if __current_context is None:
|
|
|
|
raise InvalidStateError("Drawing context must be set before setting " +
|
|
|
|
"drawing mode")
|
|
|
|
|
|
|
|
if event is not None:
|
|
|
|
__current_event = event
|
|
|
|
|
|
|
|
|
|
|
|
def initialize_gl():
|
|
|
|
"""
|
|
|
|
Initializes the OpenGL context on the Window.
|
|
|
|
"""
|
|
|
|
# Since we aren't using shaders lighting needs to be
|
|
|
|
# enabled.
|
|
|
|
glEnable(GL_LIGHTING);
|
|
|
|
glEnable(GL_LIGHT0);
|
|
|
|
|
|
|
|
# Set white background
|
|
|
|
glClearColor(255, 255, 255, 0)
|
|
|
|
|
|
|
|
|
|
|
|
def resize_gl(w, h):
|
|
|
|
"""
|
|
|
|
OpenGL resize handler used to get the current viewport size.
|
|
|
|
|
|
|
|
@param w The new width.
|
|
|
|
@param h The new height.
|
|
|
|
"""
|
|
|
|
global __WIDTH
|
|
|
|
global __HEIGHT
|
|
|
|
|
|
|
|
__WIDTH = __current_context.width()
|
|
|
|
__HEIGHT = __current_context.height()
|
|
|
|
|
|
|
|
|
|
|
|
def paint_gl():
|
|
|
|
"""
|
|
|
|
Stock PaintGL function from OpenGL that switches
|
|
|
|
on the current mode to determine what action to
|
|
|
|
perform on the current event.
|
|
|
|
"""
|
|
|
|
if (__current_mode in [Mode.ADD, Mode.EDIT, Mode.MOVE, Mode.DELETE] and
|
|
|
|
__current_event is None):
|
|
|
|
raise InvalidStateError("Event must exist for ADD, EDIT, MOVE, " +
|
|
|
|
"and DELETE")
|
|
|
|
|
|
|
|
if (__current_mode in [Mode.ADD, Mode.EDIT, Mode.MOVE, Mode.DELETE] and
|
|
|
|
__current_points is None):
|
|
|
|
raise InvalidStateError("Points must exist for ADD, EDIT, MOVE, " +
|
|
|
|
"and DELETE")
|
|
|
|
|
|
|
|
if __current_mode is Mode.ADD or __current_mode is Mode.DELETE:
|
|
|
|
# Note that drawing the points doesn't require a bounding box or
|
|
|
|
# any special context, so delete just removes the element from
|
|
|
|
# the point set, which will be redrawn here. This action
|
|
|
|
# is the same as adding a point since we just draw what is in
|
|
|
|
# the point set.
|
|
|
|
draw_points(__current_points, Color.BLUE)
|
|
|
|
elif __current_mode is Mode.EDIT:
|
|
|
|
raise NotImplementedError("Drawing for EDIT not implemented.")
|
|
|
|
elif __current_mode is Mode.MOVE:
|
|
|
|
raise NotImplementedError("Drawing for MOVE not implemented.")
|
|
|
|
elif __current_mode is Mode.DELETE:
|
|
|
|
raise NotImplementedError("Drawing for DELETE not implemented.")
|
|
|
|
|
|
|
|
|
|
|
|
def __clamp_x(x):
|
|
|
|
"""
|
|
|
|
X-coordinate clamping function that goes from mouse coordinates to
|
|
|
|
OpenGL coordinates.
|
|
|
|
|
|
|
|
@param x The x-coordinate to clamp.
|
|
|
|
@returns The clamped x coordinate.
|
|
|
|
"""
|
|
|
|
x_w = (x / (__WIDTH / 2.0) - 1.0)
|
|
|
|
return x_w
|
|
|
|
|
|
|
|
|
|
|
|
def __clamp_y(y):
|
|
|
|
"""
|
|
|
|
Y-coordinate clamping function that goes from mouse coordinates to
|
|
|
|
OpenGL coordinates.
|
|
|
|
|
|
|
|
@param y The y-coordinate to clamp.
|
|
|
|
@returns The clamped y coordinate.
|
|
|
|
"""
|
|
|
|
y_w = -1.0 * (y / (__HEIGHT / 2.0) - 1.0)
|
|
|
|
return y_w
|
|
|
|
|
|
|
|
|
|
|
|
def draw_points(point_set, color):
|
|
|
|
"""
|
|
|
|
Simple point drawing function.
|
|
|
|
|
|
|
|
Given a coordinate (x, y), and a Color enum this
|
|
|
|
function will draw the given point with the given
|
|
|
|
color.
|
|
|
|
|
|
|
|
@param point_set The PointSet to draw.
|
|
|
|
@param color The Color Enum.
|
|
|
|
"""
|
|
|
|
global __current_context
|
|
|
|
|
|
|
|
if __current_context is None:
|
|
|
|
raise InvalidStateError("Drawing context must be set before setting " +
|
|
|
|
"drawing mode")
|
|
|
|
|
|
|
|
if not isinstance(color, Color):
|
|
|
|
raise ValueError("Color must exist in the Color enumeration")
|
|
|
|
|
|
|
|
ct = COLOR_TO_RGBA[color]
|
|
|
|
|
|
|
|
glViewport(0, 0, __WIDTH, __HEIGHT)
|
|
|
|
glPointSize(__current_points.point_size)
|
|
|
|
|
|
|
|
|
|
|
|
glBegin(GL_POINTS)
|
|
|
|
glColor4f(ct[0], ct[1], ct[2], ct[3])
|
|
|
|
for point in point_set.points:
|
|
|
|
glVertex3f(__clamp_x(point.x),
|
|
|
|
__clamp_y(point.y),
|
|
|
|
0.0) # Z is currently fixed to 0
|
|
|
|
glEnd()
|
|
|
|
|
|
|
|
|