From 7418827e42761bb1d1fef77e6c69a2aaa7afab69 Mon Sep 17 00:00:00 2001 From: Christian <s224389@dtu.dk> Date: Thu, 16 Jan 2025 17:37:22 +0100 Subject: [PATCH] Added rainbow toggle button functionality for visualizing point order --- GUI_draft_live.py | 100 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 30 deletions(-) diff --git a/GUI_draft_live.py b/GUI_draft_live.py index 83011b4..f5f8bf9 100644 --- a/GUI_draft_live.py +++ b/GUI_draft_live.py @@ -126,6 +126,19 @@ class ImageGraphicsView(QGraphicsView): self.cost_image_original = 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 # -------------------------------------------------------------------- @@ -179,7 +192,7 @@ class ImageGraphicsView(QGraphicsView): self._revert_cost_to_original() 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, label="", removable=True, z_val=1, radius=self.dot_radius) else: @@ -193,8 +206,8 @@ class ImageGraphicsView(QGraphicsView): def _insert_anchor_between_subpath(self, x_new, y_new): """Find the subpath bounding (x_new,y_new) and insert the new anchor accordingly.""" - # If no path, fallback if not self._full_path_xy: + # Fallback if no path self._insert_anchor_point(-1, x_new, y_new) return @@ -214,22 +227,18 @@ class ImageGraphicsView(QGraphicsView): self._insert_anchor_point(-1, x_new, y_new) return - # 2) Identify bounding anchors by walking left / right def approx_equal(xa, ya, xb, yb, tol=1e-3): return (abs(xa - xb) < tol) and (abs(ya - yb) < tol) def is_anchor(coord): cx, cy = coord - # check if (cx,cy) is approx any anchor in self.anchor_points for (ax, ay) in self.anchor_points: if approx_equal(ax, ay, cx, cy): return True return False + # 2) Walk left left_anchor_pt = None - right_anchor_pt = None - - # walk left iL = best_idx while iL >= 0: px, py = self._full_path_xy[iL] @@ -238,7 +247,8 @@ class ImageGraphicsView(QGraphicsView): break iL -= 1 - # walk right + # 3) Walk right + right_anchor_pt = None iR = best_idx while iR < len(self._full_path_xy): px, py = self._full_path_xy[iR] @@ -247,12 +257,17 @@ class ImageGraphicsView(QGraphicsView): break iR += 1 + # fallback if missing anchors 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) 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 right_idx = None for i, (ax, ay) in enumerate(self.anchor_points): @@ -262,23 +277,14 @@ class ImageGraphicsView(QGraphicsView): right_idx = i if left_idx is None or right_idx is None: - # fallback self._insert_anchor_point(-1, x_new, y_new) return - # We want the new anchor to be inserted right after left_idx, - # 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. + # 5) Insert new point in between if left_idx < right_idx: insert_idx = left_idx + 1 else: - # means the path might be reversed, or there's some tricky indexing - # We'll just do min or max - insert_idx = max(right_idx, left_idx) + insert_idx = right_idx + 1 self._insert_anchor_point(insert_idx, x_new, y_new, label="", removable=True, z_val=1, radius=self.dot_radius) @@ -320,7 +326,6 @@ class ImageGraphicsView(QGraphicsView): # PATH BUILDING # -------------------------------------------------------------------- def _rebuild_full_path(self): - """Compute subpaths between anchors, smooth them, store all coords, display all of them.""" # Clear old path visuals for item in self.full_path_points: self.scene.removeItem(item) @@ -338,7 +343,7 @@ class ImageGraphicsView(QGraphicsView): if i == 0: big_xy.extend(sub_xy) else: - # avoid repeating the shared anchor + # Avoid repeating the shared anchor if len(sub_xy) > 1: big_xy.extend(sub_xy[1:]) @@ -348,17 +353,32 @@ class ImageGraphicsView(QGraphicsView): smoothed = savgol_filter(arr_xy, window_length=7, polyorder=1, axis=0) big_xy = smoothed.tolist() - # We now store the entire path in _full_path_xy + # Store the entire path self._full_path_xy = big_xy[:] - # Display ALL points - for (px, py) in big_xy: - path_item = LabeledPointItem(px, py, label="", radius=self.path_radius, - color=Qt.magenta, removable=False, z_value=0) + # Draw the path + n_points = len(big_xy) + for i, (px, py) in enumerate(big_xy): + 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.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: if p_item._text_item: p_item.setZValue(100) @@ -381,6 +401,17 @@ class ImageGraphicsView(QGraphicsView): return [] 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 # -------------------------------------------------------------------- @@ -403,7 +434,7 @@ class ImageGraphicsView(QGraphicsView): self.viewport().setCursor(Qt.ClosedHandCursor) return else: - # no anchor => we'll add a new point + # No anchor => we may add a new point self.setDragMode(QGraphicsView.ScrollHandDrag) self.viewport().setCursor(Qt.ClosedHandCursor) @@ -586,10 +617,19 @@ class MainWindow(QMainWindow): self.btn_clear_points.clicked.connect(self.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) self.setCentralWidget(main_widget) self.resize(900, 600) + def toggle_rainbow(self): + """Toggle the rainbow mode in the view.""" + self.image_view.toggle_rainbow() + def load_image(self): options = QFileDialog.Options() file_path, _ = QFileDialog.getOpenFileName( -- GitLab