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