|
|
|
@ -3,12 +3,14 @@ import random
|
|
|
|
|
from PyQt5.QtCore import QEvent, Qt |
|
|
|
|
from PyQt5.QtGui import QCursor |
|
|
|
|
|
|
|
|
|
from kmeans.algorithms import k_means |
|
|
|
|
|
|
|
|
|
from clusterview2.colors import Color |
|
|
|
|
from clusterview2.exceptions import ExceededWindowBoundsError |
|
|
|
|
from clusterview2.mode import Mode |
|
|
|
|
from .opengl_widget import (set_drawing_event, set_move_bb_top_left, |
|
|
|
|
set_move_bb_bottom_right, reset_move_bbs, |
|
|
|
|
viewport_height, viewport_width) |
|
|
|
|
from clusterview2.ui.opengl_widget import (set_drawing_event, set_move_bb_top_left, |
|
|
|
|
set_move_bb_bottom_right, reset_move_bbs, |
|
|
|
|
viewport_height, viewport_width) |
|
|
|
|
from clusterview2.point_manager import PointManager |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -293,64 +295,9 @@ def _handle_info_updates(ctx, event):
|
|
|
|
|
ctx.mouse_position_label.setText(f"{event.x(), event.y()}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _handle_choose_centroids(ctx, event): |
|
|
|
|
""" |
|
|
|
|
Similar to move in terms of selecting points, however this |
|
|
|
|
function assigns a random color up to the maximum number |
|
|
|
|
of centroids, and after the maximum number has been selected it will |
|
|
|
|
enable the group button. |
|
|
|
|
""" |
|
|
|
|
global _centroid_count |
|
|
|
|
def reset_colors(): |
|
|
|
|
global _remaining_colors |
|
|
|
|
|
|
|
|
|
_handle_info_updates(ctx, event) |
|
|
|
|
|
|
|
|
|
if _centroid_count == ctx.number_of_centroids.value(): |
|
|
|
|
# We have specified the number of centroids required |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
if (event.button() == Qt.LeftButton and |
|
|
|
|
event.type() == QEvent.MouseButtonPress): |
|
|
|
|
|
|
|
|
|
point = None |
|
|
|
|
|
|
|
|
|
for test_point in PointManager.point_set.points: |
|
|
|
|
if test_point.hit(event.x(), event.y()): |
|
|
|
|
point = test_point |
|
|
|
|
|
|
|
|
|
if point is None: |
|
|
|
|
# No point was found on the click, do nothing |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
if point in PointManager.centroids: |
|
|
|
|
# Centroids must be unique |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
_centroid_count += 1 |
|
|
|
|
|
|
|
|
|
color = random.choice(_remaining_colors) |
|
|
|
|
_remaining_colors.remove(color) |
|
|
|
|
|
|
|
|
|
point.color = color |
|
|
|
|
|
|
|
|
|
# Recolor the point and restash the point in centroids |
|
|
|
|
PointManager.centroids.append(point) |
|
|
|
|
|
|
|
|
|
if _centroid_count == ctx.number_of_centroids.value(): |
|
|
|
|
# Prevent the user from changing the centroids |
|
|
|
|
ctx.number_of_centroids.setEnabled(False) |
|
|
|
|
ctx.choose_centroids_button.setEnabled(False) |
|
|
|
|
ctx.unweighted_clustering_button.setEnabled(True) |
|
|
|
|
ctx.weighted_clustering_button.setEnabled(True) |
|
|
|
|
|
|
|
|
|
ctx.opengl_widget.update() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def reset_centroid_count_and_colors(): |
|
|
|
|
global _centroid_count |
|
|
|
|
global _remaining_colors |
|
|
|
|
|
|
|
|
|
_centroid_count = 0 |
|
|
|
|
_remaining_colors = [c for c in Color if c not in [Color.BLUE, Color.GREY]] |
|
|
|
|
|
|
|
|
|
for point in PointManager.point_set.points: |
|
|
|
@ -389,6 +336,27 @@ def generate_random_points(point_count, x_bound, y_bound):
|
|
|
|
|
PointManager.point_set.add_point(point[0], point[1], Color.GREY) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _handle_clustering(ctx, _): |
|
|
|
|
points = list(PointManager.point_set.points) |
|
|
|
|
|
|
|
|
|
if not ctx.clustering_solved: |
|
|
|
|
clusters = k_means(points, ctx.number_of_clusters.value(), 0.001) |
|
|
|
|
|
|
|
|
|
# We can leverage the paint function by first assigning every cluster |
|
|
|
|
# a color (for completeness) and then assigning every point in that |
|
|
|
|
# cluster the cluster's color. |
|
|
|
|
for i, cluster in enumerate(clusters): |
|
|
|
|
cluster.color = _remaining_colors[i] |
|
|
|
|
|
|
|
|
|
for point in cluster.points: |
|
|
|
|
point.color = cluster.color |
|
|
|
|
|
|
|
|
|
PointManager.clusters = clusters |
|
|
|
|
|
|
|
|
|
ctx.opengl_widget.update() |
|
|
|
|
ctx.clustering_solved = True |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Simple dispatcher to make it easy to dispatch the right mode |
|
|
|
|
# function when the OpenGL window is acted on. |
|
|
|
|
MODE_HANDLER_MAP = { |
|
|
|
@ -398,5 +366,5 @@ MODE_HANDLER_MAP = {
|
|
|
|
|
Mode.EDIT: _handle_edit_point, |
|
|
|
|
Mode.MOVE: _handle_move_points, |
|
|
|
|
Mode.DELETE: _handle_delete_point, |
|
|
|
|
Mode.CHOOSE_CENTROIDS: _handle_choose_centroids |
|
|
|
|
Mode.UNWEIGHTED_CLUSTERING: _handle_clustering |
|
|
|
|
} |
|
|
|
|