diff --git a/README.md b/README.md index 8db6d64..4615357 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,15 @@ to explore k-means. ## Usage -TODO +First install the necessary packages: + +`pip install -r requirements.txt` + +Then launch clusterview2 using: + +`python clusterview2.py` + +from the root directory. ## Development diff --git a/clusterview2/points.py b/clusterview2/points.py index db933fb..bec91fd 100644 --- a/clusterview2/points.py +++ b/clusterview2/points.py @@ -1,11 +1,12 @@ from math import floor -from .colors import Color -from .exceptions import ExceededWindowBoundsError +from kmeans.clustering.point import Point as BasePoint -# TODO: THIS WILL NEED TO BE MODIFIED TO INHEIRIT THE KMEANS POINT CLASS. +from clusterview2.colors import Color +from clusterview2.exceptions import ExceededWindowBoundsError -class Point: + +class Point(BasePoint): """ A class representing a point. A point has a point_size bounding box around @@ -33,71 +34,71 @@ class Point: raise ValueError("Point must be initialized with a color of " + "type Color.") - self.__point_size = point_size - self.__x = x - self.__y = y + self._point_size = point_size + self._x = x + self._y = y - self.__color = color + self._color = color - self.__viewport_width = viewport_width - self.__viewport_height = viewport_height + self._viewport_width = viewport_width + self._viewport_height = viewport_height - self.__calculate_hitbox() + self._calculate_hitbox() - self.__check_window_bounds(x, y) + self._check_window_bounds(x, y) - self.__selected = False + self._selected = False - self.__attributes = [] + self._attributes = [] @property def x(self): - return self.__x + return self._x @property def y(self): - return self.__y + return self._y @property def point_size(self): - return self.__point_size + return self._point_size @property def selected(self): - return self.__selected + return self._selected @property def color(self): - return self.__color + return self._color @color.setter def color(self, color): if not isinstance(color, Color): raise ValueError('Point color must be of type Color.') - self.__color = color + self._color = color @property def attributes(self): - return self.__attributes + return self._attributes def add_attribute(self, attr): - self.__attributes.append(attr) + self._attributes.append(attr) - def __calculate_hitbox(self): + def _calculate_hitbox(self): """ Calculates the hit box for the point given the current position (center) and the point size. """ half_point = floor(self.point_size / 2.0) - self.__top_left_corner = (self.__x - half_point, - self.__y + half_point) + self._top_left_corner = (self._x - half_point, + self._y + half_point) - self.__bottom_right_corner = (self.__x + half_point, - self.__y - half_point) + self._bottom_right_corner = (self._x + half_point, + self._y - half_point) - def __check_window_bounds(self, x, y): + def _check_window_bounds(self, x, y): """ Simple window bound check that raises an exception when the point (x, y) exceeds the known viewport bounds. @@ -112,8 +113,8 @@ class Point: # 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 (x > self.__viewport_width - half_point or - y > self.__viewport_height - half_point or + if (x > self._viewport_width - half_point or + y > self._viewport_height - half_point or x < half_point or y < half_point): @@ -127,14 +128,14 @@ class Point: @param dy The delta in the y direction. """ - self.__check_window_bounds(self.__x + dx, self.__y + dy) + self._check_window_bounds(self._x + dx, self._y + dy) - self.__x += dx - self.__y += dy + self._x += dx + self._y += dy # It's important to note as we move the point we need to # make sure we are constantly updating it's hitbox. - self.__calculate_hitbox() + self._calculate_hitbox() def __eq__(self, other): """ @@ -142,22 +143,22 @@ class Point: @param other The other object. """ - return (self.__x == other.x and - self.__y == other.y and - self.__color == other.color and - self.__attributes == other.attributes and - self.__point_size == other.point_size) + return (self._x == other.x and + self._y == other.y and + self._color == other.color and + self._attributes == other.attributes and + self._point_size == other.point_size) def __repr__(self): # For some reason I had to split this instead of using one giant # string chained with `+` inside of `()`. s = "= self.__top_left_corner[0] and - x <= self.__bottom_right_corner[0] and - y <= self.__top_left_corner[1] and - y >= self.__bottom_right_corner[1]) + return (x >= self._top_left_corner[0] and + x <= self._bottom_right_corner[0] and + y <= self._top_left_corner[1] and + y >= self._bottom_right_corner[1]) class Attribute: @@ -208,8 +209,8 @@ class Attribute: """ Initializes an attribute. """ - self.__name = name - self.__value = value + self._name = name + self._value = value class PointSet: @@ -230,29 +231,29 @@ class PointSet: @param viewport_height The height of the viewport for bounds calculations. """ - self.__points = [] - self.__point_size = point_size - self.__viewport_width = viewport_width - self.__viewport_height = viewport_height + self._points = [] + self._point_size = point_size + self._viewport_width = viewport_width + self._viewport_height = viewport_height def __eq__(self, other): other_points = list(other.points) - return (self.__points == other_points and - self.__point_size == other.point_size and - self.__viewport_width == other.viewport_width and - self.__viewport_height == other.viewport_height) + return (self._points == other_points and + self._point_size == other.point_size and + self._viewport_width == other.viewport_width and + self._viewport_height == other.viewport_height) def __repr__(self): s = [] - for p in self.__points: + for p in self._points: s.append(str(p)) return ",".join(s) def clear(self): - self.__points = [] + self._points = [] @property def points(self): @@ -260,37 +261,37 @@ class PointSet: Getter for points. Returns a generator for looping. """ - for point in self.__points: + for point in self._points: yield point @property def point_size(self): - return self.__point_size + return self._point_size @property def viewport_height(self): - return self.__viewport_height + return self._viewport_height @property def viewport_width(self): - return self.__viewport_width + return self._viewport_width @viewport_height.setter def viewport_height(self, height): - self.__viewport_height = height + self._viewport_height = height @viewport_width.setter def viewport_width(self, width): - self.__viewport_width = width + self._viewport_width = width def empty(self): - return len(self.__points) == 0 + return len(self._points) == 0 def clear_selection(self): """ Handy helper function to clear all selected points. """ - for p in self.__points: + for p in self._points: p.unselect() def add_point(self, x, y, color, attrs=[]): @@ -314,17 +315,17 @@ class PointSet: if not isinstance(color, Color): raise ValueError("Point color must be a Color enum.") - point = Point(x, y, color, self.__point_size, - self.__viewport_width, self.__viewport_height) + point = Point(x, y, color, self._point_size, + self._viewport_width, self._viewport_height) for attr in attrs: point.add_attribute(attr) - if point in self.__points: + if point in self._points: # Silently reject a duplicate point (same center). return - self.__points.append(point) + self._points.append(point) def remove_point(self, x, y): """ @@ -342,9 +343,9 @@ class PointSet: @param x The x-coordinate. @param y The y-coordinate. """ - for p in self.__points: + for p in self._points: if p.hit(x, y): - self.__points.remove(p) + self._points.remove(p) def groups(self): """ @@ -353,7 +354,7 @@ class PointSet: """ g = {} - for p in self.__points: + for p in self._points: if p.color not in g: # Create the key for the group color since it does # not exist. diff --git a/requirements.txt b/requirements.txt index 0a75791..6b29137 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,4 @@ PyOpenGL-accelerate==3.1.3b1 PyQt5==5.13.0 PyQt5-sip==4.19.18 -# Add the kmeans thing here. +-e git+https://git.xchg.sh/angrygoats/kmeans.git@master#egg=kmeans