Skip to content
Snippets Groups Projects
Commit 7418827e authored by Christian's avatar Christian
Browse files

Added rainbow toggle button functionality for visualizing point order

parent e7488d98
No related branches found
No related tags found
No related merge requests found
...@@ -126,6 +126,19 @@ class ImageGraphicsView(QGraphicsView): ...@@ -126,6 +126,19 @@ class ImageGraphicsView(QGraphicsView):
self.cost_image_original = None self.cost_image_original = None
self.cost_image = None self.cost_image = None
# Rainbow toggle
self._rainbow_enabled = True
def set_rainbow_enabled(self, enabled: bool):
"""Enable/disable rainbow mode, then rebuild the path."""
self._rainbow_enabled = enabled
self._rebuild_full_path()
def toggle_rainbow(self):
"""Flip the rainbow mode and rebuild path."""
self._rainbow_enabled = not self._rainbow_enabled
self._rebuild_full_path()
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# LOADING # LOADING
# -------------------------------------------------------------------- # --------------------------------------------------------------------
...@@ -179,7 +192,7 @@ class ImageGraphicsView(QGraphicsView): ...@@ -179,7 +192,7 @@ class ImageGraphicsView(QGraphicsView):
self._revert_cost_to_original() self._revert_cost_to_original()
if not self._full_path_xy: if not self._full_path_xy:
# If there's no existing path built, we just insert normally # If there's no existing path built, just insert at the end
self._insert_anchor_point(-1, x_clamped, y_clamped, self._insert_anchor_point(-1, x_clamped, y_clamped,
label="", removable=True, z_val=1, radius=self.dot_radius) label="", removable=True, z_val=1, radius=self.dot_radius)
else: else:
...@@ -193,8 +206,8 @@ class ImageGraphicsView(QGraphicsView): ...@@ -193,8 +206,8 @@ class ImageGraphicsView(QGraphicsView):
def _insert_anchor_between_subpath(self, x_new, y_new): def _insert_anchor_between_subpath(self, x_new, y_new):
"""Find the subpath bounding (x_new,y_new) and insert the new anchor accordingly.""" """Find the subpath bounding (x_new,y_new) and insert the new anchor accordingly."""
# If no path, fallback
if not self._full_path_xy: if not self._full_path_xy:
# Fallback if no path
self._insert_anchor_point(-1, x_new, y_new) self._insert_anchor_point(-1, x_new, y_new)
return return
...@@ -214,22 +227,18 @@ class ImageGraphicsView(QGraphicsView): ...@@ -214,22 +227,18 @@ class ImageGraphicsView(QGraphicsView):
self._insert_anchor_point(-1, x_new, y_new) self._insert_anchor_point(-1, x_new, y_new)
return return
# 2) Identify bounding anchors by walking left / right
def approx_equal(xa, ya, xb, yb, tol=1e-3): def approx_equal(xa, ya, xb, yb, tol=1e-3):
return (abs(xa - xb) < tol) and (abs(ya - yb) < tol) return (abs(xa - xb) < tol) and (abs(ya - yb) < tol)
def is_anchor(coord): def is_anchor(coord):
cx, cy = coord cx, cy = coord
# check if (cx,cy) is approx any anchor in self.anchor_points
for (ax, ay) in self.anchor_points: for (ax, ay) in self.anchor_points:
if approx_equal(ax, ay, cx, cy): if approx_equal(ax, ay, cx, cy):
return True return True
return False return False
# 2) Walk left
left_anchor_pt = None left_anchor_pt = None
right_anchor_pt = None
# walk left
iL = best_idx iL = best_idx
while iL >= 0: while iL >= 0:
px, py = self._full_path_xy[iL] px, py = self._full_path_xy[iL]
...@@ -238,7 +247,8 @@ class ImageGraphicsView(QGraphicsView): ...@@ -238,7 +247,8 @@ class ImageGraphicsView(QGraphicsView):
break break
iL -= 1 iL -= 1
# walk right # 3) Walk right
right_anchor_pt = None
iR = best_idx iR = best_idx
while iR < len(self._full_path_xy): while iR < len(self._full_path_xy):
px, py = self._full_path_xy[iR] px, py = self._full_path_xy[iR]
...@@ -247,12 +257,17 @@ class ImageGraphicsView(QGraphicsView): ...@@ -247,12 +257,17 @@ class ImageGraphicsView(QGraphicsView):
break break
iR += 1 iR += 1
# fallback if missing anchors
if not left_anchor_pt or not right_anchor_pt: if not left_anchor_pt or not right_anchor_pt:
# If we can't find bounding anchors => fallback
self._insert_anchor_point(-1, x_new, y_new) self._insert_anchor_point(-1, x_new, y_new)
return return
# 3) Find which anchor_points indices correspond to left_anchor_pt, right_anchor_pt # If they happen to be the same anchor, fallback
if left_anchor_pt == right_anchor_pt:
self._insert_anchor_point(-1, x_new, y_new)
return
# 4) Map these anchor coords to indices in self.anchor_points
left_idx = None left_idx = None
right_idx = None right_idx = None
for i, (ax, ay) in enumerate(self.anchor_points): for i, (ax, ay) in enumerate(self.anchor_points):
...@@ -262,23 +277,14 @@ class ImageGraphicsView(QGraphicsView): ...@@ -262,23 +277,14 @@ class ImageGraphicsView(QGraphicsView):
right_idx = i right_idx = i
if left_idx is None or right_idx is None: if left_idx is None or right_idx is None:
# fallback
self._insert_anchor_point(-1, x_new, y_new) self._insert_anchor_point(-1, x_new, y_new)
return return
# We want the new anchor to be inserted right after left_idx, # 5) Insert new point in between
# so that the subpath between left_idx and right_idx
# is effectively subdivided.
# This ensures anchor_points = [..., left_anchor, new_point, ..., right_anchor, ...]
insert_idx = right_idx
# But if left_idx < right_idx => we do insert_idx=left_idx+1
# in case we want them consecutive.
if left_idx < right_idx: if left_idx < right_idx:
insert_idx = left_idx + 1 insert_idx = left_idx + 1
else: else:
# means the path might be reversed, or there's some tricky indexing insert_idx = right_idx + 1
# We'll just do min or max
insert_idx = max(right_idx, left_idx)
self._insert_anchor_point(insert_idx, x_new, y_new, label="", removable=True, self._insert_anchor_point(insert_idx, x_new, y_new, label="", removable=True,
z_val=1, radius=self.dot_radius) z_val=1, radius=self.dot_radius)
...@@ -320,7 +326,6 @@ class ImageGraphicsView(QGraphicsView): ...@@ -320,7 +326,6 @@ class ImageGraphicsView(QGraphicsView):
# PATH BUILDING # PATH BUILDING
# -------------------------------------------------------------------- # --------------------------------------------------------------------
def _rebuild_full_path(self): def _rebuild_full_path(self):
"""Compute subpaths between anchors, smooth them, store all coords, display all of them."""
# Clear old path visuals # Clear old path visuals
for item in self.full_path_points: for item in self.full_path_points:
self.scene.removeItem(item) self.scene.removeItem(item)
...@@ -338,7 +343,7 @@ class ImageGraphicsView(QGraphicsView): ...@@ -338,7 +343,7 @@ class ImageGraphicsView(QGraphicsView):
if i == 0: if i == 0:
big_xy.extend(sub_xy) big_xy.extend(sub_xy)
else: else:
# avoid repeating the shared anchor # Avoid repeating the shared anchor
if len(sub_xy) > 1: if len(sub_xy) > 1:
big_xy.extend(sub_xy[1:]) big_xy.extend(sub_xy[1:])
...@@ -348,17 +353,32 @@ class ImageGraphicsView(QGraphicsView): ...@@ -348,17 +353,32 @@ class ImageGraphicsView(QGraphicsView):
smoothed = savgol_filter(arr_xy, window_length=7, polyorder=1, axis=0) smoothed = savgol_filter(arr_xy, window_length=7, polyorder=1, axis=0)
big_xy = smoothed.tolist() big_xy = smoothed.tolist()
# We now store the entire path in _full_path_xy # Store the entire path
self._full_path_xy = big_xy[:] self._full_path_xy = big_xy[:]
# Display ALL points # Draw the path
for (px, py) in big_xy: n_points = len(big_xy)
path_item = LabeledPointItem(px, py, label="", radius=self.path_radius, for i, (px, py) in enumerate(big_xy):
color=Qt.magenta, removable=False, z_value=0) if n_points > 1:
fraction = i / (n_points - 1)
else:
fraction = 0
# If rainbow is on, use the rainbow color; else use a constant color
if self._rainbow_enabled:
color = self._rainbow_color(fraction)
else:
color = Qt.red
path_item = LabeledPointItem(px, py, label="",
radius=self.path_radius,
color=color,
removable=False,
z_value=0)
self.full_path_points.append(path_item) self.full_path_points.append(path_item)
self.scene.addItem(path_item) self.scene.addItem(path_item)
# Keep S/E on top # Keep S/E on top if they have labels
for p_item in self.point_items: for p_item in self.point_items:
if p_item._text_item: if p_item._text_item:
p_item.setZValue(100) p_item.setZValue(100)
...@@ -381,6 +401,17 @@ class ImageGraphicsView(QGraphicsView): ...@@ -381,6 +401,17 @@ class ImageGraphicsView(QGraphicsView):
return [] return []
return [(c, r) for (r, c) in path_rc] return [(c, r) for (r, c) in path_rc]
def _rainbow_color(self, fraction):
"""
fraction: 0..1
Returns a QColor whose hue is fraction * 300 (for example),
at full saturation and full brightness.
"""
hue = int(300 * fraction) # up to 300 degrees
saturation = 255
value = 255
return QColor.fromHsv(hue, saturation, value)
# -------------------------------------------------------------------- # --------------------------------------------------------------------
# MOUSE EVENTS # MOUSE EVENTS
# -------------------------------------------------------------------- # --------------------------------------------------------------------
...@@ -403,7 +434,7 @@ class ImageGraphicsView(QGraphicsView): ...@@ -403,7 +434,7 @@ class ImageGraphicsView(QGraphicsView):
self.viewport().setCursor(Qt.ClosedHandCursor) self.viewport().setCursor(Qt.ClosedHandCursor)
return return
else: else:
# no anchor => we'll add a new point # No anchor => we may add a new point
self.setDragMode(QGraphicsView.ScrollHandDrag) self.setDragMode(QGraphicsView.ScrollHandDrag)
self.viewport().setCursor(Qt.ClosedHandCursor) self.viewport().setCursor(Qt.ClosedHandCursor)
...@@ -586,10 +617,19 @@ class MainWindow(QMainWindow): ...@@ -586,10 +617,19 @@ class MainWindow(QMainWindow):
self.btn_clear_points.clicked.connect(self.clear_points) self.btn_clear_points.clicked.connect(self.clear_points)
btn_layout.addWidget(self.btn_clear_points) btn_layout.addWidget(self.btn_clear_points)
# Toggle Rainbow
self.btn_toggle_rainbow = QPushButton("Toggle Rainbow")
self.btn_toggle_rainbow.clicked.connect(self.toggle_rainbow)
btn_layout.addWidget(self.btn_toggle_rainbow)
main_layout.addLayout(btn_layout) main_layout.addLayout(btn_layout)
self.setCentralWidget(main_widget) self.setCentralWidget(main_widget)
self.resize(900, 600) self.resize(900, 600)
def toggle_rainbow(self):
"""Toggle the rainbow mode in the view."""
self.image_view.toggle_rainbow()
def load_image(self): def load_image(self):
options = QFileDialog.Options() options = QFileDialog.Options()
file_path, _ = QFileDialog.getOpenFileName( file_path, _ = QFileDialog.getOpenFileName(
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment