Taylor Bockman
5 years ago
11 changed files with 364 additions and 105 deletions
@ -0,0 +1,63 @@ |
|||||||
|
from .mode import Mode |
||||||
|
from .opengl_widget import set_drawing_event |
||||||
|
|
||||||
|
def __handle_add_point(ctx, event): |
||||||
|
""" |
||||||
|
Event handler for the add point mode. |
||||||
|
|
||||||
|
Sets the drawing mode for the OpenGL Widget using |
||||||
|
`set_drawing_mode`, converts a point to our point |
||||||
|
representation, and adds it to the list. |
||||||
|
""" |
||||||
|
print("[ADD] GOT POINT: ({}, {})".format(event.x(), event.y())) |
||||||
|
|
||||||
|
set_drawing_event(event) |
||||||
|
ctx.update() |
||||||
|
# Convert to our point representation and add to list widget |
||||||
|
# Point representation is a class called Point with coordinates, |
||||||
|
# and attributes (currently always None) |
||||||
|
|
||||||
|
def __handle_edit_point(ctx, event): |
||||||
|
# TODO: This function and delete definitely need to make sure they are |
||||||
|
# on a point we have. |
||||||
|
# |
||||||
|
# Since points are unique consider a hashmap of points to make O(1) |
||||||
|
# lookups for addition and deletion. This list can be maintained here |
||||||
|
# in this module. It should be a dictionary - from point to |
||||||
|
# attributes in the case of algorithms that require points to have |
||||||
|
# weights or something. |
||||||
|
# |
||||||
|
# Should move the associated point in the list to the new location if |
||||||
|
# applicable. |
||||||
|
print("[EDIT] GOT POINT: ({}, {})".format(event.x(), event.y())) |
||||||
|
|
||||||
|
# Store old x, y from event |
||||||
|
set_drawing_event(event) |
||||||
|
ctx.update() |
||||||
|
# after this remove the point from the list |
||||||
|
|
||||||
|
def __handle_move_points(ctx, event): |
||||||
|
# TODO: Should move the associated points in the list to the new location. |
||||||
|
print("[MOVE] Pressed - NOTE NEED DRAG EVENT") |
||||||
|
# Store list of old points that are captured |
||||||
|
|
||||||
|
set_drawing_event(event) |
||||||
|
|
||||||
|
# Find and move all points from the old list to their new locations |
||||||
|
|
||||||
|
def __handle_delete_point(ctx, event): |
||||||
|
print("[DELETE] GOT POINT: ({}, {})".format(event.x(), event.y())) |
||||||
|
|
||||||
|
set_drawing_event(event) |
||||||
|
|
||||||
|
# Find the point from event and remove it from the list |
||||||
|
|
||||||
|
# Simple dispatcher to make it easy to dispatch the right mode |
||||||
|
# function when the OpenGL window is clicked. |
||||||
|
MODE_HANDLER_MAP = { |
||||||
|
Mode.OFF: lambda: None, |
||||||
|
Mode.ADD: __handle_add_point, |
||||||
|
Mode.EDIT: __handle_edit_point, |
||||||
|
Mode.MOVE: __handle_move_points, |
||||||
|
Mode.DELETE: __handle_delete_point |
||||||
|
} |
@ -0,0 +1,65 @@ |
|||||||
|
class Attribute: |
||||||
|
__name = None |
||||||
|
__value = None |
||||||
|
|
||||||
|
def __init__(self, name, value): |
||||||
|
""" |
||||||
|
Initializes an attribute. |
||||||
|
""" |
||||||
|
self.__name = name |
||||||
|
self.__value = value |
||||||
|
|
||||||
|
|
||||||
|
class PointSet: |
||||||
|
""" |
||||||
|
Useful point set for storing coordinates and attributes. It is |
||||||
|
backed by a set to provide nice convenience functions. |
||||||
|
""" |
||||||
|
__points = set() |
||||||
|
__attributes = {} |
||||||
|
|
||||||
|
def add_point(self, x, y, attrs=[]): |
||||||
|
""" |
||||||
|
Adds a point in screen coordinates and an optional attribute to |
||||||
|
the list. |
||||||
|
|
||||||
|
@param x The x-coordinate. |
||||||
|
@param y The y-coordinate. |
||||||
|
@param attr An optional attribute. |
||||||
|
""" |
||||||
|
|
||||||
|
if attrs != [] and not all(isinstance(x, Attribute) for x in attrs): |
||||||
|
raise ValueError("Attributes in add_point must be an " + |
||||||
|
"attribute array.") |
||||||
|
|
||||||
|
point = (x, y) |
||||||
|
self.__points.add(point) |
||||||
|
self.__attributes[point] = attrs |
||||||
|
|
||||||
|
|
||||||
|
def remove_point(self, x, y): |
||||||
|
""" |
||||||
|
Removes a point and it's attributes from the point set. |
||||||
|
""" |
||||||
|
point = (x, y) |
||||||
|
self.__points.discard(point) |
||||||
|
self.__attributes.pop(point) |
||||||
|
|
||||||
|
|
||||||
|
def attributes(self, x, y): |
||||||
|
""" |
||||||
|
Returns the attribute array for a given point. |
||||||
|
|
||||||
|
@param x The x-coordinate of the point. |
||||||
|
@param y The y-coordinate of the point. |
||||||
|
""" |
||||||
|
return self.__attributes[(x, y)] |
||||||
|
|
||||||
|
@property |
||||||
|
def points(self): |
||||||
|
""" |
||||||
|
Getter for points. Returns a generator for |
||||||
|
looping. |
||||||
|
""" |
||||||
|
for point in self.__points: |
||||||
|
yield point |
@ -1,24 +1,36 @@ |
|||||||
|
atomicwrites==1.3.0 |
||||||
|
attrs==19.1.0 |
||||||
backcall==0.1.0 |
backcall==0.1.0 |
||||||
|
coverage==4.5.4 |
||||||
Cython==0.29.13 |
Cython==0.29.13 |
||||||
decorator==4.4.0 |
decorator==4.4.0 |
||||||
entrypoints==0.3 |
entrypoints==0.3 |
||||||
flake8==3.7.8 |
flake8==3.7.8 |
||||||
|
importlib-metadata==0.19 |
||||||
ipython==7.7.0 |
ipython==7.7.0 |
||||||
ipython-genutils==0.2.0 |
ipython-genutils==0.2.0 |
||||||
jedi==0.14.1 |
jedi==0.14.1 |
||||||
mccabe==0.6.1 |
mccabe==0.6.1 |
||||||
|
more-itertools==7.2.0 |
||||||
|
packaging==19.1 |
||||||
parso==0.5.1 |
parso==0.5.1 |
||||||
pexpect==4.7.0 |
pexpect==4.7.0 |
||||||
pickleshare==0.7.5 |
pickleshare==0.7.5 |
||||||
|
pluggy==0.12.0 |
||||||
prompt-toolkit==2.0.9 |
prompt-toolkit==2.0.9 |
||||||
ptyprocess==0.6.0 |
ptyprocess==0.6.0 |
||||||
|
py==1.8.0 |
||||||
pycodestyle==2.5.0 |
pycodestyle==2.5.0 |
||||||
pyflakes==2.1.1 |
pyflakes==2.1.1 |
||||||
Pygments==2.4.2 |
Pygments==2.4.2 |
||||||
PyOpenGL==3.1.0 |
PyOpenGL==3.1.0 |
||||||
PyOpenGL-accelerate==3.1.3b1 |
PyOpenGL-accelerate==3.1.3b1 |
||||||
|
pyparsing==2.4.2 |
||||||
PyQt5==5.13.0 |
PyQt5==5.13.0 |
||||||
PyQt5-sip==4.19.18 |
PyQt5-sip==4.19.18 |
||||||
|
pytest==5.0.1 |
||||||
|
pytest-cov==2.7.1 |
||||||
six==1.12.0 |
six==1.12.0 |
||||||
traitlets==4.3.2 |
traitlets==4.3.2 |
||||||
wcwidth==0.1.7 |
wcwidth==0.1.7 |
||||||
|
zipp==0.5.2 |
||||||
|
@ -0,0 +1,52 @@ |
|||||||
|
import pytest |
||||||
|
|
||||||
|
from clusterview.points import Attribute, PointSet |
||||||
|
|
||||||
|
|
||||||
|
def test_add_to_point_set(): |
||||||
|
l = PointSet() |
||||||
|
|
||||||
|
l.add_point(1, 2) |
||||||
|
|
||||||
|
points = list(l.points) |
||||||
|
|
||||||
|
assert len(points) == 1 |
||||||
|
assert points[0] == (1, 2) |
||||||
|
assert len(l.attributes(1, 2)) == 0 |
||||||
|
|
||||||
|
def test_add_to_point_set_with_attributes(): |
||||||
|
attribute = Attribute("thing", 1) |
||||||
|
|
||||||
|
l = PointSet() |
||||||
|
l.add_point(1, 2, attrs=[attribute]) |
||||||
|
|
||||||
|
points = list(l.points) |
||||||
|
attrs = l.attributes(1, 2) |
||||||
|
|
||||||
|
assert len(points) == 1 |
||||||
|
assert points[0] == (1, 2) |
||||||
|
assert len(l.attributes(1, 2)) == 1 |
||||||
|
|
||||||
|
def test_remove_point(): |
||||||
|
attribute = Attribute("thing", 1) |
||||||
|
|
||||||
|
l = PointSet() |
||||||
|
l.add_point(1, 2, attrs=[attribute]) |
||||||
|
|
||||||
|
l.remove_point(1, 2) |
||||||
|
|
||||||
|
points = list(l.points) |
||||||
|
|
||||||
|
assert len(points) == 0 |
||||||
|
|
||||||
|
with pytest.raises(KeyError): |
||||||
|
# We expect a call to attributes on a removed |
||||||
|
# point to raise a KeyError because it no |
||||||
|
# longer exists in the point -> attribute_list |
||||||
|
# dictionary. |
||||||
|
l.attributes(1, 2) |
||||||
|
|
||||||
|
def test_attributes_must_be_array_of_attributes(): |
||||||
|
with pytest.raises(ValueError): |
||||||
|
l = PointSet() |
||||||
|
l.add_point(1, 2, attrs=[1,2,3,4,5]) |
Loading…
Reference in new issue