Browse Source

Add some really simple movement collision checking code

tb-init-ui-render
Taylor Bockman 5 years ago
parent
commit
b841124897
  1. 4
      clusterview/exceptions.py
  2. 40
      clusterview/mode_handlers.py
  3. 5
      clusterview/opengl_widget.py
  4. 40
      clusterview/points.py
  5. 34
      tests/test_point.py
  6. 20
      tests/test_point_set.py

4
clusterview/exceptions.py

@ -2,11 +2,12 @@ from PyQt5.QtWidgets import QErrorMessage
from .mode import Mode from .mode import Mode
class ExceededWindowBoundsError(Exception):
pass
class InvalidStateError(Exception): class InvalidStateError(Exception):
pass pass
class InvalidModeError(Exception): class InvalidModeError(Exception):
""" """
An exception to specify an invalid mode has been provided. An exception to specify an invalid mode has been provided.
@ -26,7 +27,6 @@ class InvalidModeError(Exception):
if mode == Mode.OFF: if mode == Mode.OFF:
super().__init__("You must select a mode before continuing.") super().__init__("You must select a mode before continuing.")
def handle_exceptions(func): def handle_exceptions(func):
""" """
A decorator designed to make exceptions thrown A decorator designed to make exceptions thrown

40
clusterview/mode_handlers.py

@ -6,7 +6,7 @@ from .mode import Mode
from .opengl_widget import (get_bb_bottom_right, get_bb_top_left, from .opengl_widget import (get_bb_bottom_right, get_bb_top_left,
set_current_points, set_drawing_event, set_current_points, set_drawing_event,
set_move_bb_top_left, set_move_bb_bottom_right, set_move_bb_top_left, set_move_bb_bottom_right,
reset_move_bbs) reset_move_bbs, viewport_height, viewport_width)
from .points import PointSet from .points import PointSet
class __ClickFlag: class __ClickFlag:
@ -33,7 +33,7 @@ __POINT_SIZE = 8
# There are a lot of module-global variables being used because of the # There are a lot of module-global variables being used because of the
# nature of state management in OpenGL. # nature of state management in OpenGL.
__point_set = PointSet(__POINT_SIZE) __point_set = PointSet(__POINT_SIZE, viewport_height(), viewport_width())
# Module level flag for left click events (used to detect a left # Module level flag for left click events (used to detect a left
# click hold drag) # click hold drag)
@ -57,7 +57,6 @@ def __refresh_point_list(ctx):
for p in __point_set.points: for p in __point_set.points:
ctx.point_list_widget.addItem("({}, {})".format(p.x, p.y)) ctx.point_list_widget.addItem("({}, {})".format(p.x, p.y))
def __handle_add_point(ctx, event): def __handle_add_point(ctx, event):
""" """
Event handler for the add point mode. Event handler for the add point mode.
@ -89,7 +88,6 @@ def __handle_add_point(ctx, event):
ctx.opengl_widget.update() ctx.opengl_widget.update()
ctx.point_list_widget.update() ctx.point_list_widget.update()
def __handle_edit_point(ctx, event): def __handle_edit_point(ctx, event):
# TODO: This function and delete definitely need to make sure they are # TODO: This function and delete definitely need to make sure they are
# on a point we have. # on a point we have.
@ -111,7 +109,6 @@ def __handle_edit_point(ctx, event):
ctx.update() ctx.update()
# after this remove the point from the list # after this remove the point from the list
def ogl_keypress_handler(ctx, event): def ogl_keypress_handler(ctx, event):
""" """
A keypress handler attached to the OpenGL widget. A keypress handler attached to the OpenGL widget.
@ -142,7 +139,6 @@ def ogl_keypress_handler(ctx, event):
ctx.opengl_widget.update() ctx.opengl_widget.update()
def __handle_move_points(ctx, event): def __handle_move_points(ctx, event):
""" """
A relatively complicated state machine that handles the process of A relatively complicated state machine that handles the process of
@ -195,21 +191,21 @@ def __handle_move_points(ctx, event):
# If we used the deltas directly the points would # If we used the deltas directly the points would
# fly off screen quickly as we got farther from our # fly off screen quickly as we got farther from our
# start. # start.
try:
# TODO: THIS IS NUTS RIGHT NOW if dx > 0:
# WE SHOULD NOT ALLOW ANY POINT TO EXCEED p.move(1, 0)
# THE MAXIMUM SIZE OF THE SCREEN. if dx < 0:
p.move(-1, 0)
if dx > 0 and dy > 0: if dy > 0:
p.move(10, 10) p.move(0, 1)
elif dx > 0 and dy == 0: if dy < 0:
p.move(10, 0) p.move(0, -1)
elif dx < 0 and dy == 0:
p.move(-10, 0) except ExceededWindowBoundsError:
elif dx == 0 and dy > 0: # This point has indicated a move would exceed
p.move(0, 10) # it's bounds, so we'll just go to the next
elif dx == 0 and dy < 0: # point.
p.move(0, -10) continue
elif (__left_click_flag is not __ClickFlag.NONE and elif (__left_click_flag is not __ClickFlag.NONE and
@ -222,8 +218,6 @@ def __handle_move_points(ctx, event):
# Satisfy the post condition by resetting the bounding box # Satisfy the post condition by resetting the bounding box
reset_move_bbs() reset_move_bbs()
ctx.point_list_widget.update() ctx.point_list_widget.update()
ctx.opengl_widget.update() ctx.opengl_widget.update()

5
clusterview/opengl_widget.py

@ -191,6 +191,11 @@ def resize_gl(w, h):
__WIDTH = __current_context.width() __WIDTH = __current_context.width()
__HEIGHT = __current_context.height() __HEIGHT = __current_context.height()
def viewport_width():
return __WIDTH
def viewport_height():
return __HEIGHT
@handle_exceptions @handle_exceptions
def paint_gl(): def paint_gl():

40
clusterview/points.py

@ -1,5 +1,7 @@
from math import floor from math import floor
from .exceptions import ExceededWindowBoundsError
class Point: class Point:
""" """
A class representing a point. A point A class representing a point. A point
@ -7,20 +9,27 @@ class Point:
it. it.
""" """
def __init__(self, x, y, point_size): def __init__(self, x, y, point_size, viewport_width, viewport_height):
""" """
Initializes a new point with a point_size bounding box. Initializes a new point with a point_size bounding box.
@param point_size The size of the point in pixels. Initialized with additional viewport data to make sure the
move function refuses to move a point outside the screen.
@param x The x-coordinate. @param x The x-coordinate.
@param y The y-coordinate. @param y The y-coordinate.
@param point_size The size of the point in pixels.
@param viewport_width The width of the viewport.
@param viewport_height The height of the viewport.
""" """
self.__point_size = point_size self.__point_size = point_size
self.__x = x self.__x = x
self.__y = y self.__y = y
self.__selected = False self.__selected = False
self.__viewport_width = viewport_width
self.__viewport_height = viewport_height
half_point = floor(point_size / 2) half_point = floor(point_size / 2.0)
self.__top_left_corner = (self.__x - half_point, self.__top_left_corner = (self.__x - half_point,
self.__y + half_point) self.__y + half_point)
@ -51,6 +60,18 @@ class Point:
@param dy The delta in the y direction. @param dy The delta in the y direction.
""" """
half_point = floor(self.point_size / 2.0)
# Screen size in pixels is always positive
# We need to include the half point here because
# the (x, y) for a point is the center of the square and we
# do not want the EDGES to exceed the viewport bounds.
if (self.__x + dx > self.__viewport_width - half_point or
self.__y + dy > self.__viewport_height - half_point or
self.__x + dx < half_point or self.__y + dy < half_point):
raise ExceededWindowBoundsError
self.__x += dx self.__x += dx
self.__y += dy self.__y += dy
@ -117,7 +138,6 @@ class Point:
y <= self.__top_left_corner[1] and y <= self.__top_left_corner[1] and
y >= self.__bottom_right_corner[1]) y >= self.__bottom_right_corner[1])
class Attribute: class Attribute:
def __init__(self, name, value): def __init__(self, name, value):
@ -127,22 +147,27 @@ class Attribute:
self.__name = name self.__name = name
self.__value = value self.__value = value
class PointSet: class PointSet:
""" """
Useful container for points backed by a set to insure point Useful container for points backed by a set to insure point
uniqueness. uniqueness.
""" """
def __init__(self, point_size): def __init__(self, point_size, viewport_width, viewport_height):
""" """
Initializes a point container with points of size point_size. Initializes a point container with points of size point_size.
@param point_size The size of the points. @param point_size The size of the points.
@param viewport_width The width of the viewport for bounds
calculations.
@param viewport_height The height of the viewport for bounds
calculations.
""" """
self.__points = set() self.__points = set()
self.__attributes = {} self.__attributes = {}
self.__point_size = point_size self.__point_size = point_size
self.__viewport_width = viewport_width
self.__viewport_height = viewport_height
@property @property
def points(self): def points(self):
@ -178,7 +203,8 @@ class PointSet:
raise ValueError("Attributes in add_point must be an " + raise ValueError("Attributes in add_point must be an " +
"attribute array.") "attribute array.")
point = Point(x, y, self.__point_size) point = Point(x, y, self.__point_size,
self.__viewport_width, self.__viewport_height)
self.__points.add(point) self.__points.add(point)
self.__attributes[point] = attrs self.__attributes[point] = attrs

34
tests/test_point.py

@ -1,7 +1,37 @@
import pytest
from clusterview.exceptions import ExceededWindowBoundsError
from clusterview.points import Point from clusterview.points import Point
def test_move_point(): def test_move_point():
p = Point(1, 2, 8) # The minimum starting position is 1/2 point away
# from the edges
p = Point(4, 4, 8, 100, 100)
p.move(1, 1) p.move(1, 1)
assert p.x == 2 and p.y == 3 assert p.x == 5 and p.y == 5
def test_move_point_outside_screen_x_positive():
p = Point(1, 2, 8, 100, 100)
with pytest.raises(ExceededWindowBoundsError):
p.move(96, 0)
def test_move_point_outside_screen_y_positive():
p = Point(1, 2, 8, 100, 100)
with pytest.raises(ExceededWindowBoundsError):
p.move(0, 95)
def test_move_point_outside_screen_x_negative():
p = Point(1, 2, 8, 100, 100)
with pytest.raises(ExceededWindowBoundsError):
p.move(-2, 0)
def test_move_point_outside_screen_y_negative():
p = Point(1, 2, 8, 100, 100)
with pytest.raises(ExceededWindowBoundsError):
p.move(0, -3)

20
tests/test_point_set.py

@ -4,13 +4,13 @@ from clusterview.points import Attribute, Point, PointSet
def test_add_to_point_set(): def test_add_to_point_set():
l = PointSet(3) l = PointSet(3, 100, 100)
l.add_point(1, 2) l.add_point(1, 2)
points = list(l.points) points = list(l.points)
p = Point(1, 2, 3) p = Point(1, 2, 3, 100, 100)
assert len(points) == 1 assert len(points) == 1
assert points[0] == p assert points[0] == p
assert len(l.attributes(p)) == 0 assert len(l.attributes(p)) == 0
@ -18,11 +18,11 @@ def test_add_to_point_set():
def test_add_to_point_set_with_attributes(): def test_add_to_point_set_with_attributes():
attribute = Attribute("thing", 1) attribute = Attribute("thing", 1)
l = PointSet(3) l = PointSet(3, 100, 100)
l.add_point(2, 3, attrs=[attribute]) l.add_point(2, 3, attrs=[attribute])
points = list(l.points) points = list(l.points)
point = Point(2, 3, 3) point = Point(2, 3, 3, 100, 100)
attrs = l.attributes(point) attrs = l.attributes(point)
assert len(points) == 1 assert len(points) == 1
@ -32,10 +32,10 @@ def test_add_to_point_set_with_attributes():
def test_remove_point_exact_click(): def test_remove_point_exact_click():
attribute = Attribute("thing", 1) attribute = Attribute("thing", 1)
l = PointSet(8) l = PointSet(8, 100, 100)
l.add_point(1, 2, attrs=[attribute]) l.add_point(1, 2, attrs=[attribute])
p = Point(1, 2, 8) p = Point(1, 2, 8, 100, 100)
l.remove_point(1, 2) l.remove_point(1, 2)
points = list(l.points) points = list(l.points)
@ -55,10 +55,10 @@ def test_remove_point_bounding_box():
""" """
attribute = Attribute("thing", 1) attribute = Attribute("thing", 1)
l = PointSet(8) l = PointSet(8, 100, 100)
l.add_point(1, 2, attrs=[attribute]) l.add_point(1, 2, attrs=[attribute])
p = Point(1, 2, 8) p = Point(1, 2, 8, 100, 100)
# The click-point (2, 1) will be inside of our point size 8 # The click-point (2, 1) will be inside of our point size 8
# bounding box. # bounding box.
@ -77,11 +77,11 @@ def test_remove_point_bounding_box():
def test_attributes_must_be_array_of_attributes(): def test_attributes_must_be_array_of_attributes():
with pytest.raises(ValueError): with pytest.raises(ValueError):
l = PointSet(8) l = PointSet(8, 100, 100)
l.add_point(1, 2, attrs=[1,2,3,4,5]) l.add_point(1, 2, attrs=[1,2,3,4,5])
def test_clear_all_selected_points(): def test_clear_all_selected_points():
l = PointSet(8) l = PointSet(8, 100, 100)
l.add_point(1, 2) l.add_point(1, 2)
l.add_point(3, 4) l.add_point(3, 4)

Loading…
Cancel
Save