From 23422b1bc64cc135f71359520ef99d9a984a7a07 Mon Sep 17 00:00:00 2001 From: Renan Date: Tue, 22 Oct 2024 22:36:33 +0300 Subject: [PATCH 01/33] ADD: sub marker list control --- invesalius/gui/task_navigator.py | 57 +++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 8b9e37bbf..8295a4ea8 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2133,6 +2133,34 @@ def __init__(self, parent, nav_hub): self, self.marker_list_ctrl.GetColumnCount() ) + # Sub List Control + self.sub_marker_list_ctrl = wx.ListCtrl(self, style=wx.LC_REPORT) + self.sub_marker_list_ctrl.InsertColumn(const.ID_COLUMN, "#") + self.sub_marker_list_ctrl.SetColumnWidth(const.ID_COLUMN, 24) + + self.sub_marker_list_ctrl.InsertColumn(const.SESSION_COLUMN, "Session") + self.sub_marker_list_ctrl.SetColumnWidth(const.SESSION_COLUMN, 51) + + self.sub_marker_list_ctrl.InsertColumn(const.MARKER_TYPE_COLUMN, "Type") + self.sub_marker_list_ctrl.SetColumnWidth(const.MARKER_TYPE_COLUMN, 77) + + self.sub_marker_list_ctrl.InsertColumn(const.LABEL_COLUMN, "Label") + self.sub_marker_list_ctrl.SetColumnWidth(const.LABEL_COLUMN, 95) + + self.sub_marker_list_ctrl.InsertColumn(const.TARGET_COLUMN, "Target") + self.sub_marker_list_ctrl.SetColumnWidth(const.TARGET_COLUMN, 45) + + self.sub_marker_list_ctrl.InsertColumn(const.Z_OFFSET_COLUMN, "Z-offset") + self.sub_marker_list_ctrl.SetColumnWidth(const.Z_OFFSET_COLUMN, 45) + + self.sub_marker_list_ctrl.InsertColumn(const.POINT_OF_INTEREST_TARGET_COLUMN, "Efield Target") + self.sub_marker_list_ctrl.SetColumnWidth(const.POINT_OF_INTEREST_TARGET_COLUMN, 45) + + self.sub_marker_list_ctrl.InsertColumn(const.MEP_COLUMN, "MEP (uV)") + self.sub_marker_list_ctrl.SetColumnWidth(const.MEP_COLUMN, 45) + #self.sub_marker_list_ctrl.Hide() + + self.sub_marker_items_list = {} # In the future, it would be better if the panel could initialize itself based on markers in MarkersControl try: self.markers.LoadState() @@ -2146,6 +2174,7 @@ def __init__(self, parent, nav_hub): group_sizer.Add(sizer_delete, 0, wx.BOTTOM | wx.ALIGN_CENTER_HORIZONTAL, 5) group_sizer.Add(sizer_main_coil, 0, wx.BOTTOM | wx.ALIGN_CENTER_HORIZONTAL, 5) group_sizer.Add(marker_list_ctrl, 0, wx.EXPAND | wx.ALL, 5) + group_sizer.Add(self.sub_marker_list_ctrl, 0, wx.EXPAND | wx.ALL, 5) group_sizer.Fit(self) self.SetSizer(group_sizer) @@ -2534,6 +2563,13 @@ def FocusOnMarker(self, idx): event.SetEventObject(self.marker_list_ctrl) self.marker_list_ctrl.GetEventHandler().ProcessEvent(event) + def populate_sub_list(self, sub_items_list): + """Populate the sub list""" + self.sub_marker_list_ctrl.DeleteAllItems() + for sub_item in sub_items_list: + #self.sub_marker_list_ctrl.InsertItem(self.sub_marker_list_ctrl.GetItemCount(), sub_item) + self.sub_marker_list_ctrl.Append(sub_item) + # Called when a marker on the list gets the focus by the user left-clicking on it. def OnMarkerFocused(self, evt): idx = self.marker_list_ctrl.GetFocusedItem() @@ -2564,6 +2600,14 @@ def OnMarkerFocused(self, evt): self.currently_focused_marker = marker self.markers.SelectMarker(marker_id) + self.sub_marker_list_ctrl.DeleteAllItems() + if idx in self.sub_marker_items_list: + self.populate_sub_list([self.sub_marker_items_list[idx]]) + self.sub_marker_list_ctrl.Show() + else: + self.sub_marker_list_ctrl.Hide() + self.Layout() # Refresh layout + self.Update() # Called when a marker on the list loses the focus by the user left-clicking on another marker. # @@ -3332,16 +3376,21 @@ def _AddMarker(self, marker, render, focus): list_entry.append(round(marker.y, 1)) list_entry.append(round(marker.z, 1)) - self.marker_list_ctrl.Append(list_entry) - self.marker_list_ctrl.SetItemData(num_items, key) + if marker.marker_type == MarkerType.BRAIN_TARGET: + key_sub = 0 + if len(self.sub_marker_items_list) > 0: + key_sub = list(self.sub_marker_items_list.keys())[-1] + 1 + self.sub_marker_items_list[key_sub] = list_entry.copy() + else: + self.marker_list_ctrl.Append(list_entry) + self.marker_list_ctrl.SetItemData(num_items, key) + data_map_entry = list_entry.copy() # Add the UUID to the entry in itemDataMap data_map_entry.append(marker.marker_uuid) self.itemDataMap[key] = data_map_entry - if marker.marker_type == MarkerType.BRAIN_TARGET: - self.marker_list_ctrl.SetItemBackgroundColour(num_items, wx.Colour(102, 178, 255)) self.marker_list_ctrl.EnsureVisible(num_items) From 5e1cbb02498a3511c0cb4fd39cc167a0351a0c62 Mon Sep 17 00:00:00 2001 From: Renan Date: Wed, 23 Oct 2024 11:16:29 +0300 Subject: [PATCH 02/33] WIP --- invesalius/gui/task_navigator.py | 42 +++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 8295a4ea8..6020ba7ed 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2250,6 +2250,9 @@ def _DeleteMarker(self, marker): self.marker_list_ctrl.DeleteItem(idx) print("_DeleteMarker:", deleted_marker_uuid) + if idx in self.sub_marker_items_list: + self.sub_marker_items_list.pop(idx) + # Delete the marker from itemDataMap for key, data in self.itemDataMap.items(): current_uuid = data[-1] @@ -2566,8 +2569,7 @@ def FocusOnMarker(self, idx): def populate_sub_list(self, sub_items_list): """Populate the sub list""" self.sub_marker_list_ctrl.DeleteAllItems() - for sub_item in sub_items_list: - #self.sub_marker_list_ctrl.InsertItem(self.sub_marker_list_ctrl.GetItemCount(), sub_item) + for sub_item in sub_items_list[0]: self.sub_marker_list_ctrl.Append(sub_item) # Called when a marker on the list gets the focus by the user left-clicking on it. @@ -2931,7 +2933,14 @@ def __get_marker(self, idx): """ For an index in self.marker_list_ctrl, returns the corresponding marker """ - marker_id = self.__get_marker_id(idx) + for marker in self.markers.list: + filtered_dict = {key: value for key, value in self.itemDataMap.items() if isinstance(key, int)} + for data in filtered_dict.items(): + current_uuid = data[-1] + if current_uuid == marker.marker_uuid: + marker_id = self.markers.list.index(marker) + + #marker_id = self.__get_marker_id(idx) return self.markers.list[marker_id] def ChangeColor(self, evt): @@ -3062,6 +3071,10 @@ def OnDeleteSelectedMarkers(self, evt=None): if result != wx.ID_OK: return + for index in indexes: + if index in self.sub_marker_items_list: + self.sub_marker_items_list.pop(index) + self.__delete_multiple_markers(indexes) # Re-focus on the marker with the same index as the first marker that was selected before deletion. @@ -3354,11 +3367,6 @@ def _AddMarker(self, marker, render, focus): # Add marker to the marker list in GUI and to the itemDataMap. num_items = self.marker_list_ctrl.GetItemCount() - key = 0 - if len(self.itemDataMap) > 0: - # If itemDataMap is not empty, set the new key as last key + 1 - key = list(self.itemDataMap.keys())[-1] + 1 - list_entry = ["" for _ in range(0, const.X_COLUMN)] list_entry[const.ID_COLUMN] = num_items list_entry[const.SESSION_COLUMN] = str(marker.session_id) @@ -3377,11 +3385,21 @@ def _AddMarker(self, marker, render, focus): list_entry.append(round(marker.z, 1)) if marker.marker_type == MarkerType.BRAIN_TARGET: - key_sub = 0 - if len(self.sub_marker_items_list) > 0: - key_sub = list(self.sub_marker_items_list.keys())[-1] + 1 - self.sub_marker_items_list[key_sub] = list_entry.copy() + focused_marker_idx = self.marker_list_ctrl.GetFocusedItem() + num_items = focused_marker_idx + if focused_marker_idx in self.sub_marker_items_list: + key = str(focused_marker_idx)+"."+str(len(self.sub_marker_items_list[focused_marker_idx])) + list_entry[const.ID_COLUMN] = key + self.sub_marker_items_list[focused_marker_idx].append(list_entry.copy()) + else: + key = str(focused_marker_idx)+".0" + list_entry[const.ID_COLUMN] = key + self.sub_marker_items_list[focused_marker_idx] = [list_entry.copy()] else: + key = 0 + if len(self.itemDataMap) > 0: + # If itemDataMap is not empty, set the new key as last key + 1 + key = len(self.itemDataMap.keys()) + 1 self.marker_list_ctrl.Append(list_entry) self.marker_list_ctrl.SetItemData(num_items, key) From 6897a0d1551f0d0242fbe705a262cab431186320 Mon Sep 17 00:00:00 2001 From: Renan Date: Wed, 23 Oct 2024 11:19:50 +0300 Subject: [PATCH 03/33] WIP --- invesalius/gui/task_navigator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 6020ba7ed..e8308aae0 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2935,7 +2935,7 @@ def __get_marker(self, idx): """ for marker in self.markers.list: filtered_dict = {key: value for key, value in self.itemDataMap.items() if isinstance(key, int)} - for data in filtered_dict.items(): + for key, data in filtered_dict.items(): current_uuid = data[-1] if current_uuid == marker.marker_uuid: marker_id = self.markers.list.index(marker) From e0d4e5f284a22b2e0f8f66b8ade7de2b8c087794 Mon Sep 17 00:00:00 2001 From: rmatsuda Date: Mon, 28 Oct 2024 17:39:38 +0200 Subject: [PATCH 04/33] MOD: store brain_targets inside marker --- invesalius/data/markers/marker.py | 6 ++ .../data/visualization/marker_visualizer.py | 9 +++ invesalius/gui/task_navigator.py | 77 ++++++++++++++----- invesalius/navigation/markers.py | 22 +++++- 4 files changed, 93 insertions(+), 21 deletions(-) diff --git a/invesalius/data/markers/marker.py b/invesalius/data/markers/marker.py index f220cd239..827036855 100644 --- a/invesalius/data/markers/marker.py +++ b/invesalius/data/markers/marker.py @@ -78,6 +78,7 @@ class Marker: # #TODO: add a reference to original coil marker to relate it to MEP # in micro Volts (but scale in milli Volts for display) mep_value: float = dataclasses.field(default=None) + brain_target_list: list = dataclasses.field(default_factory=list) # x, y, z can be jointly accessed as position @property @@ -225,6 +226,8 @@ def to_dict(self): "z_rotation": self.z_rotation, "z_offset": self.z_offset, "mep_value": self.mep_value, + "brain_target_list": self.brain_target_list, + "marker_uuid": self.marker_uuid, } def from_dict(self, d): @@ -270,6 +273,8 @@ def from_dict(self, d): z_rotation = d.get("z_rotation", 0.0) is_point_of_interest = d.get("is_point_of_interest", False) mep_value = d.get("mep_value", None) + brain_target_list = d.get("brain_target_list", []) + marker_uuid = d.get("marker_uuid", "") self.size = d["size"] self.label = d["label"] @@ -286,6 +291,7 @@ def from_dict(self, d): self.z_offset = z_offset self.z_rotation = z_rotation self.mep_value = mep_value + self.brain_target_list = brain_target_list return self diff --git a/invesalius/data/visualization/marker_visualizer.py b/invesalius/data/visualization/marker_visualizer.py index 0d56755a1..dad8d0378 100644 --- a/invesalius/data/visualization/marker_visualizer.py +++ b/invesalius/data/visualization/marker_visualizer.py @@ -66,6 +66,7 @@ def __bind_events(self): Publisher.subscribe(self.HideMarkers, "Hide markers") Publisher.subscribe(self.ShowMarkers, "Show markers") Publisher.subscribe(self.DeleteMarkers, "Delete markers") + Publisher.subscribe(self.DeleteBrainMarkers, "Delete brain markers") Publisher.subscribe(self.DeleteMarker, "Delete marker") Publisher.subscribe(self.SetCameraToFocusOnMarker, "Set camera to focus on marker") Publisher.subscribe(self.HighlightMarker, "Highlight marker") @@ -244,6 +245,14 @@ def ShowMarkers(self, markers): if not self.is_navigating: self.interactor.Render() + def DeleteBrainMarkers(self, brain_markers): + for marker in brain_markers: + actor = marker.visualization["actor"] + self.renderer.RemoveActor(actor) + + if not self.is_navigating: + self.interactor.Render() + def DeleteMarkers(self, markers): for marker in markers: actor = marker.visualization["actor"] diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 870d5a0ef..1633af4ad 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2224,6 +2224,8 @@ def __bind_events(self): Publisher.subscribe(self._UpdateMarkerLabel, "Update marker label") Publisher.subscribe(self._UpdateMEP, "Update marker mep") + Publisher.subscribe(self.SetVectorField, "Set vector field") + def __get_selected_items(self): """ Returns a (possibly empty) list of the selected items in the list control. @@ -2239,7 +2241,7 @@ def __get_selected_items(self): return selection def __delete_multiple_markers(self, indexes): - marker_ids = [self.__get_marker_id(idx) for idx in indexes] + marker_ids = [int(self.marker_list_ctrl.GetItem(idx, const.ID_COLUMN).GetText()) for idx in indexes] self.markers.DeleteMultiple(marker_ids) def _DeleteMarker(self, marker): @@ -2284,9 +2286,12 @@ def _DeleteMultiple(self, markers): if current_uuid == deleted_uuid: deleted_keys.append(key) - - self.marker_list_ctrl.DeleteItem(idx) - deleted_ids.append(marker.marker_id) + if idx: + self.marker_list_ctrl.DeleteItem(idx) + deleted_ids.append(marker.marker_id) + else: + self.marker_list_ctrl.DeleteItem(0) + deleted_ids.append(marker.marker_id) # Remove all the deleted markers from itemDataMap for key in deleted_keys: @@ -2384,6 +2389,9 @@ def UpdateSeedCoordinates( def UpdateCortexMarker(self, CoGposition, CoGorientation): self.cortex_position_orientation = CoGposition + CoGorientation + def SetVectorField(self, vector_field): + self.vector_field = vector_field + def OnMouseRightDown(self, evt): focused_marker_idx = self.marker_list_ctrl.GetFocusedItem() focused_marker = self.__get_marker(focused_marker_idx) @@ -2568,8 +2576,18 @@ def FocusOnMarker(self, idx): def populate_sub_list(self, sub_items_list): """Populate the sub list""" self.sub_marker_list_ctrl.DeleteAllItems() - for sub_item in sub_items_list[0]: - self.sub_marker_list_ctrl.Append(sub_item) + focused_marker_idx = self.marker_list_ctrl.GetFocusedItem() + marker = self.__get_marker(focused_marker_idx) + num_items = focused_marker_idx + for i, sub_item in enumerate(sub_items_list): + list_entry = ["" for _ in range(0, const.X_COLUMN)] + list_entry[const.ID_COLUMN] = str(num_items)+"."+str(i) + list_entry[const.SESSION_COLUMN] = str(marker.session_id) + list_entry[const.MARKER_TYPE_COLUMN] = MarkerType.BRAIN_TARGET.human_readable + list_entry[const.LABEL_COLUMN] = marker.label + list_entry[const.TARGET_COLUMN] = "Yes" if marker.is_target else "" + list_entry[const.MEP_COLUMN] = str(marker.mep_value) if marker.mep_value else "" + self.sub_marker_list_ctrl.Append(list_entry) # Called when a marker on the list gets the focus by the user left-clicking on it. def OnMarkerFocused(self, evt): @@ -2580,7 +2598,7 @@ def OnMarkerFocused(self, evt): return marker_id = self.__get_marker_id(idx) - marker = self.markers.list[marker_id] + marker = self.__get_marker(idx) # XXX: There seems to be a bug in WxPython when selecting multiple items on the list using, # e.g., shift and page-up/page-down keys. The bug is that the EVT_LIST_ITEM_SELECTED event @@ -2602,8 +2620,8 @@ def OnMarkerFocused(self, evt): self.currently_focused_marker = marker self.markers.SelectMarker(marker_id) self.sub_marker_list_ctrl.DeleteAllItems() - if idx in self.sub_marker_items_list: - self.populate_sub_list([self.sub_marker_items_list[idx]]) + if marker.brain_target_list: + self.populate_sub_list(marker.brain_target_list) self.sub_marker_list_ctrl.Show() else: self.sub_marker_list_ctrl.Hide() @@ -2618,7 +2636,7 @@ def OnMarkerUnfocused(self, evt): def SetCameraToFocusOnMarker(self, evt): idx = self.marker_list_ctrl.GetFocusedItem() - marker = self.markers.list[idx] + marker = self.__get_marker(idx) Publisher.sendMessage("Set camera to focus on marker", marker=marker) def OnCreateCoilTargetFromLandmark(self, evt): @@ -2825,9 +2843,9 @@ def OnSetEfieldBrainTarget(self, evt): def OnCreateBrainTargetFromLandmark(self, evt): list_index = self.marker_list_ctrl.GetFocusedItem() - marker = self.__get_marker(list_index) - position = marker.position - orientation = marker.orientation + marker_coil = self.__get_marker(list_index) + position = marker_coil.position + orientation = marker_coil.orientation dialog = dlg.CreateBrainTargetDialog( marker=position + orientation, brain_actor=self.brain_actor @@ -2851,7 +2869,9 @@ def OnCreateBrainTargetFromLandmark(self, evt): # should probably be modified to reflect that. marker_type=MarkerType.BRAIN_TARGET, ) + marker.marker_uuid = str(uuid.uuid4()) self.markers.AddMarker(marker, render=True, focus=True) + marker_coil.brain_target_list.append(marker.to_dict()) for position, orientation in zip(brain_position_list, brain_orientation_list): marker = self.CreateMarker( @@ -2859,7 +2879,9 @@ def OnCreateBrainTargetFromLandmark(self, evt): orientation=list(orientation), marker_type=MarkerType.BRAIN_TARGET, ) + marker.marker_uuid = str(uuid.uuid4()) self.markers.AddMarker(marker, render=True, focus=False) + marker_coil.brain_target_list.append(marker.to_dict()) dialog.Destroy() @@ -2925,6 +2947,13 @@ def __get_marker_id(self, idx): """ For an index in self.marker_list_ctrl, returns the corresponding marker_id """ + filtered_dict = {key: value for key, value in self.itemDataMap.items() if isinstance(key, int)} + if len(list(filtered_dict.items())) > idx: + for marker in self.markers.list: + current_uuid = list(filtered_dict.items())[idx][-1][-1] + if current_uuid == marker.marker_uuid: + marker_id = self.markers.list.index(marker) + return int(marker_id) list_item = self.marker_list_ctrl.GetItem(idx, const.ID_COLUMN) return int(list_item.GetText()) @@ -2932,14 +2961,18 @@ def __get_marker(self, idx): """ For an index in self.marker_list_ctrl, returns the corresponding marker """ + filtered_dict = {key: value for key, value in self.itemDataMap.items() if isinstance(key, int)} for marker in self.markers.list: - filtered_dict = {key: value for key, value in self.itemDataMap.items() if isinstance(key, int)} - for key, data in filtered_dict.items(): - current_uuid = data[-1] - if current_uuid == marker.marker_uuid: - marker_id = self.markers.list.index(marker) + try: + current_uuid = list(filtered_dict.items())[idx][-1][-1] + except: + marker_id = self.__get_marker_id(idx) + return self.markers.list[marker_id] + if current_uuid == marker.marker_uuid: + marker_id = self.markers.list.index(marker) + return self.markers.list[marker_id] - #marker_id = self.__get_marker_id(idx) + marker_id = self.__get_marker_id(idx) return self.markers.list[marker_id] def ChangeColor(self, evt): @@ -3034,6 +3067,11 @@ def OnSelectMarkerByActor(self, actor): self.marker_list_ctrl.Select(idx_old, on=False) # Focus and select the marker in the list control. + filtered_dict = {key: value for key, value in self.itemDataMap.items() if isinstance(key, int)} + for k, v in filtered_dict.items(): + if v[-1] == m.marker_uuid: + idx = v[0] + self.marker_list_ctrl.Focus(idx) self.marker_list_ctrl.Select(idx, on=True) break @@ -3325,6 +3363,7 @@ def CreateMarker( z_offset=0.0, z_rotation=0.0, mep_value=None, + brain_target_list=[], ): """ Create a new marker object. diff --git a/invesalius/navigation/markers.py b/invesalius/navigation/markers.py index 85c3246dc..e968698ae 100644 --- a/invesalius/navigation/markers.py +++ b/invesalius/navigation/markers.py @@ -90,16 +90,23 @@ def Clear(self): # currently not be used outside this class. def DeleteMarker(self, marker_id, render=True): marker = self.list[marker_id] - + indices_to_delete = [marker_id] if marker.is_target: self.UnsetTarget(marker_id) if marker.is_point_of_interest: self.UnsetPointOfInterest(marker_id) + if marker.brain_target_list: + for m in marker.brain_target_list: + for i in self.list: + if i.marker_uuid == m["marker_uuid"]: + indices_to_delete.append(i.marker_id) if render: Publisher.sendMessage("Delete marker", marker=marker) - del self.list[marker_id] + indices_to_delete.sort(reverse=True) + for i in indices_to_delete: + del self.list[i] if render: for idx, m in enumerate(self.list): @@ -112,10 +119,18 @@ def DeleteMarker(self, marker_id, render=True): def DeleteMultiple(self, marker_ids): markers = [] + brain_markers = [] for m_id in sorted(marker_ids, reverse=True): markers.append(self.list[m_id]) + if self.list[m_id].brain_target_list: + for m in self.list[m_id].brain_target_list: + for i in self.list: + if i.marker_uuid == m["marker_uuid"]: + brain_markers.append(self.list[i.marker_id]) self.DeleteMarker(m_id, render=False) + brain_markers.sort(key=lambda x: x.marker_id, reverse=True) + Publisher.sendMessage("Delete brain markers", brain_markers=brain_markers) Publisher.sendMessage("Delete markers", markers=markers) for idx, m in enumerate(self.list): @@ -295,3 +310,6 @@ def CreateCoilTargetFromCoilPose(self, marker): new_marker.marker_type = MarkerType.COIL_TARGET self.AddMarker(new_marker) + + def AddBrainTarget(self, marker, new_brain_target): + marker.brain_target_list(new_brain_target) From 75a7ee56147686d897b1e38459057a0644df4655 Mon Sep 17 00:00:00 2001 From: rmatsuda Date: Mon, 28 Oct 2024 17:40:10 +0200 Subject: [PATCH 05/33] RUFF --- invesalius/gui/task_navigator.py | 33 ++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 1633af4ad..860b6e01c 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2152,12 +2152,14 @@ def __init__(self, parent, nav_hub): self.sub_marker_list_ctrl.InsertColumn(const.Z_OFFSET_COLUMN, "Z-offset") self.sub_marker_list_ctrl.SetColumnWidth(const.Z_OFFSET_COLUMN, 45) - self.sub_marker_list_ctrl.InsertColumn(const.POINT_OF_INTEREST_TARGET_COLUMN, "Efield Target") + self.sub_marker_list_ctrl.InsertColumn( + const.POINT_OF_INTEREST_TARGET_COLUMN, "Efield Target" + ) self.sub_marker_list_ctrl.SetColumnWidth(const.POINT_OF_INTEREST_TARGET_COLUMN, 45) self.sub_marker_list_ctrl.InsertColumn(const.MEP_COLUMN, "MEP (uV)") self.sub_marker_list_ctrl.SetColumnWidth(const.MEP_COLUMN, 45) - #self.sub_marker_list_ctrl.Hide() + # self.sub_marker_list_ctrl.Hide() self.sub_marker_items_list = {} # In the future, it would be better if the panel could initialize itself based on markers in MarkersControl @@ -2241,7 +2243,9 @@ def __get_selected_items(self): return selection def __delete_multiple_markers(self, indexes): - marker_ids = [int(self.marker_list_ctrl.GetItem(idx, const.ID_COLUMN).GetText()) for idx in indexes] + marker_ids = [ + int(self.marker_list_ctrl.GetItem(idx, const.ID_COLUMN).GetText()) for idx in indexes + ] self.markers.DeleteMultiple(marker_ids) def _DeleteMarker(self, marker): @@ -2581,7 +2585,7 @@ def populate_sub_list(self, sub_items_list): num_items = focused_marker_idx for i, sub_item in enumerate(sub_items_list): list_entry = ["" for _ in range(0, const.X_COLUMN)] - list_entry[const.ID_COLUMN] = str(num_items)+"."+str(i) + list_entry[const.ID_COLUMN] = str(num_items) + "." + str(i) list_entry[const.SESSION_COLUMN] = str(marker.session_id) list_entry[const.MARKER_TYPE_COLUMN] = MarkerType.BRAIN_TARGET.human_readable list_entry[const.LABEL_COLUMN] = marker.label @@ -2947,7 +2951,9 @@ def __get_marker_id(self, idx): """ For an index in self.marker_list_ctrl, returns the corresponding marker_id """ - filtered_dict = {key: value for key, value in self.itemDataMap.items() if isinstance(key, int)} + filtered_dict = { + key: value for key, value in self.itemDataMap.items() if isinstance(key, int) + } if len(list(filtered_dict.items())) > idx: for marker in self.markers.list: current_uuid = list(filtered_dict.items())[idx][-1][-1] @@ -2961,7 +2967,9 @@ def __get_marker(self, idx): """ For an index in self.marker_list_ctrl, returns the corresponding marker """ - filtered_dict = {key: value for key, value in self.itemDataMap.items() if isinstance(key, int)} + filtered_dict = { + key: value for key, value in self.itemDataMap.items() if isinstance(key, int) + } for marker in self.markers.list: try: current_uuid = list(filtered_dict.items())[idx][-1][-1] @@ -3067,7 +3075,9 @@ def OnSelectMarkerByActor(self, actor): self.marker_list_ctrl.Select(idx_old, on=False) # Focus and select the marker in the list control. - filtered_dict = {key: value for key, value in self.itemDataMap.items() if isinstance(key, int)} + filtered_dict = { + key: value for key, value in self.itemDataMap.items() if isinstance(key, int) + } for k, v in filtered_dict.items(): if v[-1] == m.marker_uuid: idx = v[0] @@ -3426,11 +3436,15 @@ def _AddMarker(self, marker, render, focus): focused_marker_idx = self.marker_list_ctrl.GetFocusedItem() num_items = focused_marker_idx if focused_marker_idx in self.sub_marker_items_list: - key = str(focused_marker_idx)+"."+str(len(self.sub_marker_items_list[focused_marker_idx])) + key = ( + str(focused_marker_idx) + + "." + + str(len(self.sub_marker_items_list[focused_marker_idx])) + ) list_entry[const.ID_COLUMN] = key self.sub_marker_items_list[focused_marker_idx].append(list_entry.copy()) else: - key = str(focused_marker_idx)+".0" + key = str(focused_marker_idx) + ".0" list_entry[const.ID_COLUMN] = key self.sub_marker_items_list[focused_marker_idx] = [list_entry.copy()] else: @@ -3447,7 +3461,6 @@ def _AddMarker(self, marker, render, focus): data_map_entry.append(marker.marker_uuid) self.itemDataMap[key] = data_map_entry - self.marker_list_ctrl.EnsureVisible(num_items) # Focus on the added marker. From d064ca826ef450b759435441177c37a1856b00a9 Mon Sep 17 00:00:00 2001 From: Renan Date: Mon, 28 Oct 2024 20:31:33 +0200 Subject: [PATCH 06/33] ADD: UUID to get idx --- invesalius/constants.py | 7 ++-- invesalius/data/markers/marker.py | 1 + invesalius/gui/task_navigator.py | 53 ++++++++++++++----------------- invesalius/navigation/markers.py | 4 +-- 4 files changed, 31 insertions(+), 34 deletions(-) diff --git a/invesalius/constants.py b/invesalius/constants.py index 45694d9ba..05f86da5b 100644 --- a/invesalius/constants.py +++ b/invesalius/constants.py @@ -802,9 +802,10 @@ Z_OFFSET_COLUMN = 5 POINT_OF_INTEREST_TARGET_COLUMN = 6 MEP_COLUMN = 7 -X_COLUMN = 8 -Y_COLUMN = 9 -Z_COLUMN = 10 +UUID = 8 +X_COLUMN = 9 +Y_COLUMN = 10 +Z_COLUMN = 11 # ------------ Navigation defaults ------------------- diff --git a/invesalius/data/markers/marker.py b/invesalius/data/markers/marker.py index 827036855..88cd30713 100644 --- a/invesalius/data/markers/marker.py +++ b/invesalius/data/markers/marker.py @@ -292,6 +292,7 @@ def from_dict(self, d): self.z_rotation = z_rotation self.mep_value = mep_value self.brain_target_list = brain_target_list + self.marker_uuid = marker_uuid return self diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 860b6e01c..2fb837df4 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2112,6 +2112,9 @@ def __init__(self, parent, nav_hub): marker_list_ctrl.InsertColumn(const.MEP_COLUMN, "MEP (uV)") marker_list_ctrl.SetColumnWidth(const.MEP_COLUMN, 45) + marker_list_ctrl.InsertColumn(const.UUID, "UUID") + marker_list_ctrl.SetColumnWidth(const.UUID, 45) + if self.session.GetConfig("debug"): marker_list_ctrl.InsertColumn(const.X_COLUMN, "X") marker_list_ctrl.SetColumnWidth(const.X_COLUMN, 45) @@ -2159,6 +2162,9 @@ def __init__(self, parent, nav_hub): self.sub_marker_list_ctrl.InsertColumn(const.MEP_COLUMN, "MEP (uV)") self.sub_marker_list_ctrl.SetColumnWidth(const.MEP_COLUMN, 45) + + self.sub_marker_list_ctrl.InsertColumn(const.UUID, "UUID") + self.sub_marker_list_ctrl.SetColumnWidth(const.UUID, 45) # self.sub_marker_list_ctrl.Hide() self.sub_marker_items_list = {} @@ -2307,11 +2313,12 @@ def _DeleteMultiple(self, markers): num_items = self.marker_list_ctrl.GetItemCount() for n in range(num_items): m_id = self.__get_marker_id(n) - reduction_in_m_id = 0 - for d_id in deleted_ids: - if m_id > d_id: - reduction_in_m_id += 1 - self.marker_list_ctrl.SetItem(n, const.ID_COLUMN, str(m_id - reduction_in_m_id)) + if m_id: + reduction_in_m_id = 0 + for d_id in deleted_ids: + if m_id > d_id: + reduction_in_m_id += 1 + self.marker_list_ctrl.SetItem(n, const.ID_COLUMN, str(m_id - reduction_in_m_id)) self.marker_list_ctrl.Show() @@ -2591,6 +2598,7 @@ def populate_sub_list(self, sub_items_list): list_entry[const.LABEL_COLUMN] = marker.label list_entry[const.TARGET_COLUMN] = "Yes" if marker.is_target else "" list_entry[const.MEP_COLUMN] = str(marker.mep_value) if marker.mep_value else "" + list_entry[const.UUID] = str(marker.marker_uuid) if marker.marker_uuid else "" self.sub_marker_list_ctrl.Append(list_entry) # Called when a marker on the list gets the focus by the user left-clicking on it. @@ -2951,15 +2959,11 @@ def __get_marker_id(self, idx): """ For an index in self.marker_list_ctrl, returns the corresponding marker_id """ - filtered_dict = { - key: value for key, value in self.itemDataMap.items() if isinstance(key, int) - } - if len(list(filtered_dict.items())) > idx: - for marker in self.markers.list: - current_uuid = list(filtered_dict.items())[idx][-1][-1] - if current_uuid == marker.marker_uuid: - marker_id = self.markers.list.index(marker) - return int(marker_id) + current_uuid = self.marker_list_ctrl.GetItem(idx, const.UUID).GetText() + for marker in self.markers.list: + if current_uuid == marker.marker_uuid: + marker_id = self.markers.list.index(marker) + return int(marker_id) list_item = self.marker_list_ctrl.GetItem(idx, const.ID_COLUMN) return int(list_item.GetText()) @@ -2967,15 +2971,8 @@ def __get_marker(self, idx): """ For an index in self.marker_list_ctrl, returns the corresponding marker """ - filtered_dict = { - key: value for key, value in self.itemDataMap.items() if isinstance(key, int) - } + current_uuid = self.marker_list_ctrl.GetItem(idx, const.UUID).GetText() for marker in self.markers.list: - try: - current_uuid = list(filtered_dict.items())[idx][-1][-1] - except: - marker_id = self.__get_marker_id(idx) - return self.markers.list[marker_id] if current_uuid == marker.marker_uuid: marker_id = self.markers.list.index(marker) return self.markers.list[marker_id] @@ -3074,13 +3071,10 @@ def OnSelectMarkerByActor(self, actor): if idx_old != -1 and idx_old != idx: self.marker_list_ctrl.Select(idx_old, on=False) - # Focus and select the marker in the list control. - filtered_dict = { - key: value for key, value in self.itemDataMap.items() if isinstance(key, int) - } - for k, v in filtered_dict.items(): - if v[-1] == m.marker_uuid: - idx = v[0] + current_uuid = self.marker_list_ctrl.GetItem(idx, const.UUID).GetText() + for i in range(self.marker_list_ctrl.GetItemCount()): + if current_uuid == self.marker_list_ctrl.GetItem(i, const.UUID).GetText(): + idx = i self.marker_list_ctrl.Focus(idx) self.marker_list_ctrl.Select(idx, on=True) @@ -3426,6 +3420,7 @@ def _AddMarker(self, marker, render, focus): "Yes" if marker.is_point_of_interest else "" ) list_entry[const.MEP_COLUMN] = str(marker.mep_value) if marker.mep_value else "" + list_entry[const.UUID] = str(marker.marker_uuid) if marker.marker_uuid else "" if self.session.GetConfig("debug"): list_entry.append(round(marker.x, 1)) diff --git a/invesalius/navigation/markers.py b/invesalius/navigation/markers.py index e968698ae..62ba50102 100644 --- a/invesalius/navigation/markers.py +++ b/invesalius/navigation/markers.py @@ -129,8 +129,8 @@ def DeleteMultiple(self, marker_ids): brain_markers.append(self.list[i.marker_id]) self.DeleteMarker(m_id, render=False) - brain_markers.sort(key=lambda x: x.marker_id, reverse=True) - Publisher.sendMessage("Delete brain markers", brain_markers=brain_markers) + if brain_markers: + Publisher.sendMessage("Delete brain markers", brain_markers=brain_markers) Publisher.sendMessage("Delete markers", markers=markers) for idx, m in enumerate(self.list): From 0216e9b5adbb2d81a70486396eb02d55abd95436 Mon Sep 17 00:00:00 2001 From: Renan Date: Mon, 28 Oct 2024 20:56:53 +0200 Subject: [PATCH 07/33] FIX: uudi OnSelectMarkerByActor --- invesalius/gui/task_navigator.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 2fb837df4..7347ed2dd 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -3071,7 +3071,7 @@ def OnSelectMarkerByActor(self, actor): if idx_old != -1 and idx_old != idx: self.marker_list_ctrl.Select(idx_old, on=False) - current_uuid = self.marker_list_ctrl.GetItem(idx, const.UUID).GetText() + current_uuid = m.marker_uuid for i in range(self.marker_list_ctrl.GetItemCount()): if current_uuid == self.marker_list_ctrl.GetItem(i, const.UUID).GetText(): idx = i @@ -3429,19 +3429,20 @@ def _AddMarker(self, marker, render, focus): if marker.marker_type == MarkerType.BRAIN_TARGET: focused_marker_idx = self.marker_list_ctrl.GetFocusedItem() - num_items = focused_marker_idx - if focused_marker_idx in self.sub_marker_items_list: - key = ( - str(focused_marker_idx) - + "." - + str(len(self.sub_marker_items_list[focused_marker_idx])) - ) - list_entry[const.ID_COLUMN] = key - self.sub_marker_items_list[focused_marker_idx].append(list_entry.copy()) - else: - key = str(focused_marker_idx) + ".0" - list_entry[const.ID_COLUMN] = key - self.sub_marker_items_list[focused_marker_idx] = [list_entry.copy()] + if focused_marker_idx>=0: + num_items = focused_marker_idx + if focused_marker_idx in self.sub_marker_items_list: + key = ( + str(focused_marker_idx) + + "." + + str(len(self.sub_marker_items_list[focused_marker_idx])) + ) + list_entry[const.ID_COLUMN] = key + self.sub_marker_items_list[focused_marker_idx].append(list_entry.copy()) + else: + key = str(focused_marker_idx) + ".0" + list_entry[const.ID_COLUMN] = key + self.sub_marker_items_list[focused_marker_idx] = [list_entry.copy()] else: key = 0 if len(self.itemDataMap) > 0: From 20f8c3d4211541df22164c7d073877ef6f9b627c Mon Sep 17 00:00:00 2001 From: Renan Date: Mon, 28 Oct 2024 21:24:43 +0200 Subject: [PATCH 08/33] FIX: delete based on UUID --- invesalius/gui/task_navigator.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 7347ed2dd..3d640450f 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2249,9 +2249,13 @@ def __get_selected_items(self): return selection def __delete_multiple_markers(self, indexes): - marker_ids = [ - int(self.marker_list_ctrl.GetItem(idx, const.ID_COLUMN).GetText()) for idx in indexes - ] + marker_ids = [] + for idx in indexes: + current_uuid = self.marker_list_ctrl.GetItem(idx, const.UUID).GetText() + for marker in self.markers.list: + if current_uuid == marker.marker_uuid: + marker_id = self.markers.list.index(marker) + marker_ids.append(marker_id) self.markers.DeleteMultiple(marker_ids) def _DeleteMarker(self, marker): @@ -2296,6 +2300,11 @@ def _DeleteMultiple(self, markers): if current_uuid == deleted_uuid: deleted_keys.append(key) + + current_uuid = marker.marker_uuid + for i in range(self.marker_list_ctrl.GetItemCount()): + if current_uuid == self.marker_list_ctrl.GetItem(i, const.UUID).GetText(): + idx = i if idx: self.marker_list_ctrl.DeleteItem(idx) deleted_ids.append(marker.marker_id) @@ -3123,8 +3132,8 @@ def OnDeleteSelectedMarkers(self, evt=None): first_deleted_index = indexes[0] first_existing_index = ( first_deleted_index - if first_deleted_index < len(self.markers.list) - else len(self.markers.list) - 1 + if first_deleted_index < self.marker_list_ctrl.GetItemCount() + else self.marker_list_ctrl.GetItemCount() - 1 ) self.FocusOnMarker(first_existing_index) From 77e89fc2a397679ea31d78248d11cc09f86cf597 Mon Sep 17 00:00:00 2001 From: rmatsuda Date: Fri, 1 Nov 2024 16:06:49 +0200 Subject: [PATCH 09/33] MOD: just append brain_target_list instead of create new marker --- invesalius/data/markers/marker.py | 14 +++++++++ invesalius/gui/task_navigator.py | 51 +++++++++++++++++++++++-------- 2 files changed, 52 insertions(+), 13 deletions(-) diff --git a/invesalius/data/markers/marker.py b/invesalius/data/markers/marker.py index 88cd30713..0db5e3651 100644 --- a/invesalius/data/markers/marker.py +++ b/invesalius/data/markers/marker.py @@ -210,6 +210,20 @@ def to_csv_row(self): ) return res + def to_brain_targets_dict(self): + return { + "position": self.position, + "orientation": self.orientation, + "colour": self.colour, + "size": self.size, + "label": self.label, + "is_target": self.is_target, + "marker_type": self.marker_type.value, + "session_id": self.session_id, + "mep_value": self.mep_value, + "marker_uuid": self.marker_uuid, + } + def to_dict(self): return { "position": self.position, diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 3d640450f..61bf69783 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2412,6 +2412,30 @@ def UpdateCortexMarker(self, CoGposition, CoGorientation): def SetVectorField(self, vector_field): self.vector_field = vector_field + # TODO: Add this to new markers from mtms + # marker_target = self.markers.FindTarget() + # if not marker_target: + # return + # + # for vector in self.vector_field: + # position = vector["position"], + # orientation = vector["orientation"], + # colour = vector["color"], + # length_multiplier = vector["length"], + # + # marker = self.CreateMarker( + # position=position, + # orientation=orientation, + # colour=colour, + # size=length_multiplier, + # label=str(marker_target.label), + # marker_type=MarkerType.BRAIN_TARGET, + # ) + # marker.marker_uuid = str(uuid.uuid4()) + # marker_target.brain_target_list.append(marker.to_brain_targets_dict()) + # + # self.markers.SaveState() + def OnMouseRightDown(self, evt): focused_marker_idx = self.marker_list_ctrl.GetFocusedItem() focused_marker = self.__get_marker(focused_marker_idx) @@ -2602,12 +2626,12 @@ def populate_sub_list(self, sub_items_list): for i, sub_item in enumerate(sub_items_list): list_entry = ["" for _ in range(0, const.X_COLUMN)] list_entry[const.ID_COLUMN] = str(num_items) + "." + str(i) - list_entry[const.SESSION_COLUMN] = str(marker.session_id) + list_entry[const.SESSION_COLUMN] = str(marker.brain_target_list[i]['session_id']) list_entry[const.MARKER_TYPE_COLUMN] = MarkerType.BRAIN_TARGET.human_readable - list_entry[const.LABEL_COLUMN] = marker.label - list_entry[const.TARGET_COLUMN] = "Yes" if marker.is_target else "" - list_entry[const.MEP_COLUMN] = str(marker.mep_value) if marker.mep_value else "" - list_entry[const.UUID] = str(marker.marker_uuid) if marker.marker_uuid else "" + list_entry[const.LABEL_COLUMN] = marker.brain_target_list[i]['label'] + list_entry[const.TARGET_COLUMN] = "Yes" if marker.brain_target_list[i]['is_target'] else "" + list_entry[const.MEP_COLUMN] = str(marker.brain_target_list[i]['mep_value']) if marker.brain_target_list[i]['mep_value'] else "" + list_entry[const.UUID] = str(marker.brain_target_list[i]['marker_uuid']) if marker.brain_target_list[i]['marker_uuid'] else "" self.sub_marker_list_ctrl.Append(list_entry) # Called when a marker on the list gets the focus by the user left-clicking on it. @@ -2891,8 +2915,8 @@ def OnCreateBrainTargetFromLandmark(self, evt): marker_type=MarkerType.BRAIN_TARGET, ) marker.marker_uuid = str(uuid.uuid4()) - self.markers.AddMarker(marker, render=True, focus=True) - marker_coil.brain_target_list.append(marker.to_dict()) + marker.label = str(marker_coil.label) + marker_coil.brain_target_list.append(marker.to_brain_targets_dict()) for position, orientation in zip(brain_position_list, brain_orientation_list): marker = self.CreateMarker( @@ -2901,9 +2925,9 @@ def OnCreateBrainTargetFromLandmark(self, evt): marker_type=MarkerType.BRAIN_TARGET, ) marker.marker_uuid = str(uuid.uuid4()) - self.markers.AddMarker(marker, render=True, focus=False) - marker_coil.brain_target_list.append(marker.to_dict()) - + marker.label = str(marker_coil.label) + marker_coil.brain_target_list.append(marker.to_brain_targets_dict()) + self.markers.SaveState() dialog.Destroy() def OnMenuRemoveEfieldTarget(self, evt): @@ -3031,8 +3055,10 @@ def OnSetBrainTarget(self, evt): size=0.05, marker_type=MarkerType.BRAIN_TARGET, ) - self.markers.AddMarker(new_marker, render=True, focus=True) - + new_marker.marker_uuid = str(uuid.uuid4()) + new_marker.label = str(marker.label) + marker.brain_target_list.append(new_marker.to_brain_targets_dict()) + self.markers.SaveState() dialog.Destroy() def OnSendBrainTarget(self, evt): @@ -3376,7 +3402,6 @@ def CreateMarker( z_offset=0.0, z_rotation=0.0, mep_value=None, - brain_target_list=[], ): """ Create a new marker object. From 371559e11fcc1cbbf90188555a091ed0389e4da5 Mon Sep 17 00:00:00 2001 From: rmatsuda Date: Fri, 1 Nov 2024 16:07:29 +0200 Subject: [PATCH 10/33] RUFF --- invesalius/gui/task_navigator.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 61bf69783..dddc46c85 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2626,12 +2626,22 @@ def populate_sub_list(self, sub_items_list): for i, sub_item in enumerate(sub_items_list): list_entry = ["" for _ in range(0, const.X_COLUMN)] list_entry[const.ID_COLUMN] = str(num_items) + "." + str(i) - list_entry[const.SESSION_COLUMN] = str(marker.brain_target_list[i]['session_id']) + list_entry[const.SESSION_COLUMN] = str(marker.brain_target_list[i]["session_id"]) list_entry[const.MARKER_TYPE_COLUMN] = MarkerType.BRAIN_TARGET.human_readable - list_entry[const.LABEL_COLUMN] = marker.brain_target_list[i]['label'] - list_entry[const.TARGET_COLUMN] = "Yes" if marker.brain_target_list[i]['is_target'] else "" - list_entry[const.MEP_COLUMN] = str(marker.brain_target_list[i]['mep_value']) if marker.brain_target_list[i]['mep_value'] else "" - list_entry[const.UUID] = str(marker.brain_target_list[i]['marker_uuid']) if marker.brain_target_list[i]['marker_uuid'] else "" + list_entry[const.LABEL_COLUMN] = marker.brain_target_list[i]["label"] + list_entry[const.TARGET_COLUMN] = ( + "Yes" if marker.brain_target_list[i]["is_target"] else "" + ) + list_entry[const.MEP_COLUMN] = ( + str(marker.brain_target_list[i]["mep_value"]) + if marker.brain_target_list[i]["mep_value"] + else "" + ) + list_entry[const.UUID] = ( + str(marker.brain_target_list[i]["marker_uuid"]) + if marker.brain_target_list[i]["marker_uuid"] + else "" + ) self.sub_marker_list_ctrl.Append(list_entry) # Called when a marker on the list gets the focus by the user left-clicking on it. @@ -3463,7 +3473,7 @@ def _AddMarker(self, marker, render, focus): if marker.marker_type == MarkerType.BRAIN_TARGET: focused_marker_idx = self.marker_list_ctrl.GetFocusedItem() - if focused_marker_idx>=0: + if focused_marker_idx >= 0: num_items = focused_marker_idx if focused_marker_idx in self.sub_marker_items_list: key = ( From 6c8d98fd8e05d0444e0cb4f0e3e76cd70577a8fc Mon Sep 17 00:00:00 2001 From: Renan Date: Sun, 3 Nov 2024 14:50:55 +0200 Subject: [PATCH 11/33] MOD: visualize brain targets with vector_field_visualizer --- .../data/visualization/marker_visualizer.py | 8 ++++---- .../visualization/vector_field_visualizer.py | 6 ++++-- invesalius/gui/task_navigator.py | 19 +++++++++++++------ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/invesalius/data/visualization/marker_visualizer.py b/invesalius/data/visualization/marker_visualizer.py index dad8d0378..377370295 100644 --- a/invesalius/data/visualization/marker_visualizer.py +++ b/invesalius/data/visualization/marker_visualizer.py @@ -76,7 +76,7 @@ def __bind_events(self): Publisher.subscribe(self.UnsetTarget, "Unset target") Publisher.subscribe(self.SetTargetTransparency, "Set target transparency") Publisher.subscribe(self.SetCoilAtTarget, "Coil at target") - Publisher.subscribe(self.UpdateVectorField, "Update vector field") + Publisher.subscribe(self.UpdateBrainTargets, "Update brain targets") Publisher.subscribe(self.UpdateNavigationStatus, "Navigation status") Publisher.subscribe(self.UpdateTargetMode, "Set target mode") @@ -86,13 +86,13 @@ def UpdateNavigationStatus(self, nav_status, vis_status): def UpdateTargetMode(self, enabled=False): self.is_target_mode = enabled - def UpdateVectorField(self): + def UpdateBrainTargets(self, brain_targets): """ Update the vector field assembly to reflect the current vector field. """ # Create a new vector field assembly. - new_vector_field_assembly = self.vector_field_visualizer.CreateVectorFieldAssembly() - + new_vector_field_assembly = self.vector_field_visualizer.CreateVectorFieldAssembly(brain_targets) + self.vector_field_assembly.SetVisibility(1) # Replace the old vector field assembly with the new one. self.actor_factory.ReplaceActor( self.renderer, self.vector_field_assembly, new_vector_field_assembly diff --git a/invesalius/data/visualization/vector_field_visualizer.py b/invesalius/data/visualization/vector_field_visualizer.py index 34f22a682..c86f0ea23 100644 --- a/invesalius/data/visualization/vector_field_visualizer.py +++ b/invesalius/data/visualization/vector_field_visualizer.py @@ -27,10 +27,12 @@ def SetVectorField(self, vector_field): self.vector_field = vector_field Publisher.sendMessage("Update vector field") - def CreateVectorFieldAssembly(self): + def CreateVectorFieldAssembly(self, vector_field=None): """ Create an assembly for the current vector field. """ + if not vector_field: + vector_field = self.vector_field assembly = vtk.vtkAssembly() actors = [ @@ -40,7 +42,7 @@ def CreateVectorFieldAssembly(self): colour=vector["color"], length_multiplier=vector["length"], ) - for vector in self.vector_field + for vector in vector_field ] for actor in actors: diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index dddc46c85..d01444897 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2623,6 +2623,7 @@ def populate_sub_list(self, sub_items_list): focused_marker_idx = self.marker_list_ctrl.GetFocusedItem() marker = self.__get_marker(focused_marker_idx) num_items = focused_marker_idx + brain_targets = [] for i, sub_item in enumerate(sub_items_list): list_entry = ["" for _ in range(0, const.X_COLUMN)] list_entry[const.ID_COLUMN] = str(num_items) + "." + str(i) @@ -2643,6 +2644,14 @@ def populate_sub_list(self, sub_items_list): else "" ) self.sub_marker_list_ctrl.Append(list_entry) + x, y, z = marker.brain_target_list[i]["position"] + brain_targets.append({ + "position": [x, -y, z], + "orientation": marker.brain_target_list[i]["orientation"], + "color": marker.brain_target_list[i]["colour"], + "length": marker.brain_target_list[i]["size"] + }) + Publisher.sendMessage("Update brain targets", brain_targets=brain_targets) # Called when a marker on the list gets the focus by the user left-clicking on it. def OnMarkerFocused(self, evt): @@ -2918,14 +2927,11 @@ def OnCreateBrainTargetFromLandmark(self, evt): marker = self.CreateMarker( position=position, orientation=orientation, - # XXX: Setting the marker type to 'brain target' is inconsistent with the variable names above ('coil_position_list' etc.); - # however, the dialog shown to the user by this function should be used exclusively for creating brain targets, hence the - # variable naming (and the internal logic of the dialog where it currently returns both coil targets and brain targets) - # should probably be modified to reflect that. marker_type=MarkerType.BRAIN_TARGET, + size=1, + label=str(marker_coil.label), ) marker.marker_uuid = str(uuid.uuid4()) - marker.label = str(marker_coil.label) marker_coil.brain_target_list.append(marker.to_brain_targets_dict()) for position, orientation in zip(brain_position_list, brain_orientation_list): @@ -2933,9 +2939,10 @@ def OnCreateBrainTargetFromLandmark(self, evt): position=list(position), orientation=list(orientation), marker_type=MarkerType.BRAIN_TARGET, + size=1, + label=str(marker_coil.label), ) marker.marker_uuid = str(uuid.uuid4()) - marker.label = str(marker_coil.label) marker_coil.brain_target_list.append(marker.to_brain_targets_dict()) self.markers.SaveState() dialog.Destroy() From d69441063f06389d473f57b6c9ab576c198a7346 Mon Sep 17 00:00:00 2001 From: Renan Date: Sun, 3 Nov 2024 15:40:46 +0200 Subject: [PATCH 12/33] ADD: mtms coords are storaged --- invesalius/data/markers/marker.py | 3 ++ invesalius/gui/task_navigator.py | 47 +++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/invesalius/data/markers/marker.py b/invesalius/data/markers/marker.py index 0db5e3651..d811156db 100644 --- a/invesalius/data/markers/marker.py +++ b/invesalius/data/markers/marker.py @@ -222,6 +222,9 @@ def to_brain_targets_dict(self): "session_id": self.session_id, "mep_value": self.mep_value, "marker_uuid": self.marker_uuid, + "x_mtms": self.x_mtms, + "y_mtms": self.y_mtms, + "r_mtms": self.r_mtms, } def to_dict(self): diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index d01444897..28d97a53d 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2905,14 +2905,46 @@ def OnSetEfieldBrainTarget(self, evt): orientation=orientation, ) + def transform_to_mtms(self, coil_position, coil_orientation_euler, brain_position): + """ + Transforms the brain position from InVesalius coordinates to the coil's coordinate system. + + Parameters: + - coil_position: array-like, shape (3,) + The position of the coil in the world coordinate system [x, y, z]. + - coil_orientation_euler: array-like, shape (3,) + The orientation of the coil in Euler angles [roll, pitch, yaw] in radians. + - brain_position: array-like, shape (3,) + The position of the brain in the world coordinate system [x, y, z]. + + Returns: + - brain_position_in_coil_coords: numpy array, shape (3,) + The brain position in the coil's coordinate system. + """ + import invesalius.data.transformations as tr + # Convert inputs to numpy arrays + coil_position = np.array(coil_position) + brain_position = np.array(brain_position) + + # Convert Euler angles to rotation matrix + coil_rotation_matrix = tr.euler_matrix(coil_orientation_euler[0], coil_orientation_euler[1], coil_orientation_euler[2], 'sxyz') + + # Step 1: Translate brain position to the coil's origin + translated_position = brain_position - coil_position + + # Step 2: Rotate the translated position into the coil's coordinate system + brain_position_in_coil_coords = np.dot(coil_rotation_matrix[:3, :3].T, translated_position) + + return brain_position_in_coil_coords + def OnCreateBrainTargetFromLandmark(self, evt): list_index = self.marker_list_ctrl.GetFocusedItem() marker_coil = self.__get_marker(list_index) - position = marker_coil.position - orientation = marker_coil.orientation + position_coil = marker_coil.position + orientation_coil = marker_coil.orientation dialog = dlg.CreateBrainTargetDialog( - marker=position + orientation, brain_actor=self.brain_actor + marker=position_coil + orientation_coil, brain_actor=self.brain_actor ) if dialog.ShowModal() == wx.ID_OK: ( @@ -2932,6 +2964,11 @@ def OnCreateBrainTargetFromLandmark(self, evt): label=str(marker_coil.label), ) marker.marker_uuid = str(uuid.uuid4()) + #EXAMPLE. TODO with mtms + mtms_coords = self.transform_to_mtms(position_coil, orientation, position) + marker.x_mtms = np.round(mtms_coords[0],1) + marker.y_mtms = np.round(mtms_coords[1],1) + marker.r_mtms = np.round(orientation[2],0) marker_coil.brain_target_list.append(marker.to_brain_targets_dict()) for position, orientation in zip(brain_position_list, brain_orientation_list): @@ -2943,6 +2980,10 @@ def OnCreateBrainTargetFromLandmark(self, evt): label=str(marker_coil.label), ) marker.marker_uuid = str(uuid.uuid4()) + mtms_coords = self.transform_to_mtms(position_coil, orientation, position) + marker.x_mtms = np.round(mtms_coords[0], 1) + marker.y_mtms = np.round(mtms_coords[1], 1) + marker.r_mtms = np.round(orientation[2], 0) marker_coil.brain_target_list.append(marker.to_brain_targets_dict()) self.markers.SaveState() dialog.Destroy() From ee40a28b603bccced949c5362a0c8028a36f758d Mon Sep 17 00:00:00 2001 From: Renan Date: Sun, 3 Nov 2024 17:25:13 +0200 Subject: [PATCH 13/33] FIX: parse for load markers --- invesalius/gui/task_navigator.py | 58 ++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 28d97a53d..c14534b23 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -3283,19 +3283,65 @@ def OnCreateMarker( def ParseValue(self, value): value = value.strip() - # Check for integer, float, string encapsulated by quotes, and None. + # Handle None, booleans, empty list, and basic types if value == "None": return None + if value == "True": + return True + if value == "False": + return False + if value == "[]": + return [] + + # Handle lists and dictionaries + if value.startswith("[") and value.endswith("]"): + return self._parse_list(value) + if value.startswith("{") and value.endswith("}"): + return self._parse_dict(value) + + # Try to convert to int or float try: - if "." in value: + if '.' in value or 'e' in value.lower(): return float(value) return int(value) - except ValueError: - # Check for strings marked by quotes. - if value.startswith('"') and value.endswith('"'): + # Handle quoted strings + if (value.startswith('"') and value.endswith('"')) or (value.startswith("'") and value.endswith("'")): return value[1:-1] - return value + return value # Return as is if not recognized + + def _parse_list(self, list_str): + """Parse a list from string format.""" + return [self.ParseValue(el.strip()) for el in self._split_by_outer_commas(list_str[1:-1].strip())] + + def _parse_dict(self, dict_str): + """Parse a dictionary from string format.""" + items = self._split_by_outer_commas(dict_str[1:-1].strip()) + return {self.ParseValue(kv.split(":", 1)[0].strip()): self.ParseValue(kv.split(":", 1)[1].strip()) for kv in + items} + + def _split_by_outer_commas(self, string): + """Split a string by commas that are not inside brackets or braces.""" + elements = [] + depth = 0 + current_element = [] + + for char in string: + if char in "[{": + depth += 1 + elif char in "]}" and depth > 0: + depth -= 1 + + if char == ',' and depth == 0: + elements.append(''.join(current_element).strip()) + current_element = [] + else: + current_element.append(char) + + if current_element: + elements.append(''.join(current_element).strip()) + + return elements def GetMarkersFromFile(self, filename, overwrite_image_fiducials): try: From 5b6ef78deb7cd2c05c039bb63a80a93ec7616d1e Mon Sep 17 00:00:00 2001 From: Renan Date: Sun, 3 Nov 2024 20:51:00 +0200 Subject: [PATCH 14/33] ADD: enable right click on brain targets --- invesalius/constants.py | 9 ++ invesalius/gui/task_navigator.py | 185 ++++++++++++++++++------------- 2 files changed, 120 insertions(+), 74 deletions(-) diff --git a/invesalius/constants.py b/invesalius/constants.py index 05f86da5b..75912ac82 100644 --- a/invesalius/constants.py +++ b/invesalius/constants.py @@ -807,6 +807,15 @@ Y_COLUMN = 10 Z_COLUMN = 11 +# The column order in the brain marker panel +# +BRAIN_ID_COLUMN = 0 +BRAIN_SESSION_COLUMN = 1 +BRAIN_MARKER_TYPE_COLUMN = 2 +BRAIN_LABEL_COLUMN = 3 +BRAIN_MEP_COLUMN = 4 +BRAIN_UUID = 5 + # ------------ Navigation defaults ------------------- MARKER_COLOUR = (1.0, 1.0, 0.0) diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index c14534b23..2305d2834 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2136,38 +2136,28 @@ def __init__(self, parent, nav_hub): ) # Sub List Control - self.sub_marker_list_ctrl = wx.ListCtrl(self, style=wx.LC_REPORT) - self.sub_marker_list_ctrl.InsertColumn(const.ID_COLUMN, "#") - self.sub_marker_list_ctrl.SetColumnWidth(const.ID_COLUMN, 24) + brain_targets_list_ctrl = wx.ListCtrl(self, style=wx.LC_REPORT) + brain_targets_list_ctrl.InsertColumn(const.BRAIN_ID_COLUMN, "#") + brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_ID_COLUMN, 24) - self.sub_marker_list_ctrl.InsertColumn(const.SESSION_COLUMN, "Session") - self.sub_marker_list_ctrl.SetColumnWidth(const.SESSION_COLUMN, 51) + brain_targets_list_ctrl.InsertColumn(const.BRAIN_SESSION_COLUMN, "Session") + brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_SESSION_COLUMN, 51) - self.sub_marker_list_ctrl.InsertColumn(const.MARKER_TYPE_COLUMN, "Type") - self.sub_marker_list_ctrl.SetColumnWidth(const.MARKER_TYPE_COLUMN, 77) + brain_targets_list_ctrl.InsertColumn(const.BRAIN_MARKER_TYPE_COLUMN, "Type") + brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_MARKER_TYPE_COLUMN, 77) - self.sub_marker_list_ctrl.InsertColumn(const.LABEL_COLUMN, "Label") - self.sub_marker_list_ctrl.SetColumnWidth(const.LABEL_COLUMN, 95) + brain_targets_list_ctrl.InsertColumn(const.BRAIN_LABEL_COLUMN, "Label") + brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_LABEL_COLUMN, 95) - self.sub_marker_list_ctrl.InsertColumn(const.TARGET_COLUMN, "Target") - self.sub_marker_list_ctrl.SetColumnWidth(const.TARGET_COLUMN, 45) + brain_targets_list_ctrl.InsertColumn(const.BRAIN_MEP_COLUMN, "MEP (uV)") + brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_MEP_COLUMN, 45) - self.sub_marker_list_ctrl.InsertColumn(const.Z_OFFSET_COLUMN, "Z-offset") - self.sub_marker_list_ctrl.SetColumnWidth(const.Z_OFFSET_COLUMN, 45) + brain_targets_list_ctrl.InsertColumn(const.BRAIN_UUID, "UUID") + brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_UUID, 45) + # self.brain_targets_list_ctrl.Hide() - self.sub_marker_list_ctrl.InsertColumn( - const.POINT_OF_INTEREST_TARGET_COLUMN, "Efield Target" - ) - self.sub_marker_list_ctrl.SetColumnWidth(const.POINT_OF_INTEREST_TARGET_COLUMN, 45) - - self.sub_marker_list_ctrl.InsertColumn(const.MEP_COLUMN, "MEP (uV)") - self.sub_marker_list_ctrl.SetColumnWidth(const.MEP_COLUMN, 45) - - self.sub_marker_list_ctrl.InsertColumn(const.UUID, "UUID") - self.sub_marker_list_ctrl.SetColumnWidth(const.UUID, 45) - # self.sub_marker_list_ctrl.Hide() - - self.sub_marker_items_list = {} + brain_targets_list_ctrl.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnMouseRightDownBrainTargets) + self.brain_targets_list_ctrl = brain_targets_list_ctrl # In the future, it would be better if the panel could initialize itself based on markers in MarkersControl try: self.markers.LoadState() @@ -2181,7 +2171,7 @@ def __init__(self, parent, nav_hub): group_sizer.Add(sizer_delete, 0, wx.BOTTOM | wx.ALIGN_CENTER_HORIZONTAL, 5) group_sizer.Add(sizer_main_coil, 0, wx.BOTTOM | wx.ALIGN_CENTER_HORIZONTAL, 5) group_sizer.Add(marker_list_ctrl, 0, wx.EXPAND | wx.ALL, 5) - group_sizer.Add(self.sub_marker_list_ctrl, 0, wx.EXPAND | wx.ALL, 5) + group_sizer.Add(brain_targets_list_ctrl, 0, wx.EXPAND | wx.ALL, 5) group_sizer.Fit(self) self.SetSizer(group_sizer) @@ -2265,9 +2255,6 @@ def _DeleteMarker(self, marker): self.marker_list_ctrl.DeleteItem(idx) print("_DeleteMarker:", deleted_marker_uuid) - if idx in self.sub_marker_items_list: - self.sub_marker_items_list.pop(idx) - # Delete the marker from itemDataMap for key, data in self.itemDataMap.items(): current_uuid = data[-1] @@ -2583,6 +2570,40 @@ def OnMouseRightDown(self, evt): self.PopupMenu(menu_id) menu_id.Destroy() + def OnMouseRightDownBrainTargets(self, evt): + focused_marker_idx = self.brain_targets_list_ctrl.GetFocusedItem() + focused_marker = self.currently_focused_marker.brain_target_list[focused_marker_idx] + unique_menu_id = 1 + + # Check if the currently focused marker is the active target. + is_active_target = focused_marker["is_target"] + + # Create the context menu. + menu_id = wx.Menu() + + edit_id = menu_id.Append(unique_menu_id, _("Change label")) # Use non-zero ID + menu_id.Bind(wx.EVT_MENU, self.ChangeLabelBrainTarget, edit_id) + + delete_id = menu_id.Append(unique_menu_id + 2, _("Delete")) + menu_id.Bind(wx.EVT_MENU, self.OnDeleteSelectedBrainTarget, delete_id) + + menu_id.AppendSeparator() + + mep_menu_item = menu_id.Append(unique_menu_id + 3, _("Change MEP value")) + menu_id.Bind(wx.EVT_MENU, self.OnMenuChangeMEPBrainTarget, mep_menu_item) + + + if has_mTMS: + send_brain_target_menu_item = menu_id.Append( + unique_menu_id + 5, _("Send brain target to mTMS") + ) + menu_id.Bind(wx.EVT_MENU, self.OnSendBrainTarget, send_brain_target_menu_item) + + menu_id.AppendSeparator() + + self.PopupMenu(menu_id) + menu_id.Destroy() + # Programmatically set the focus on the marker with the given index, simulating left click. def FocusOnMarker(self, idx): # Deselect the previously focused marker. @@ -2619,31 +2640,28 @@ def FocusOnMarker(self, idx): def populate_sub_list(self, sub_items_list): """Populate the sub list""" - self.sub_marker_list_ctrl.DeleteAllItems() + self.brain_targets_list_ctrl.DeleteAllItems() focused_marker_idx = self.marker_list_ctrl.GetFocusedItem() marker = self.__get_marker(focused_marker_idx) num_items = focused_marker_idx brain_targets = [] for i, sub_item in enumerate(sub_items_list): - list_entry = ["" for _ in range(0, const.X_COLUMN)] - list_entry[const.ID_COLUMN] = str(num_items) + "." + str(i) - list_entry[const.SESSION_COLUMN] = str(marker.brain_target_list[i]["session_id"]) - list_entry[const.MARKER_TYPE_COLUMN] = MarkerType.BRAIN_TARGET.human_readable - list_entry[const.LABEL_COLUMN] = marker.brain_target_list[i]["label"] - list_entry[const.TARGET_COLUMN] = ( - "Yes" if marker.brain_target_list[i]["is_target"] else "" - ) - list_entry[const.MEP_COLUMN] = ( + list_entry = ["" for _ in range(0, const.BRAIN_UUID+1)] + list_entry[const.BRAIN_ID_COLUMN] = str(num_items) + "." + str(i) + list_entry[const.BRAIN_SESSION_COLUMN] = str(marker.brain_target_list[i]["session_id"]) + list_entry[const.BRAIN_MARKER_TYPE_COLUMN] = MarkerType.BRAIN_TARGET.human_readable + list_entry[const.BRAIN_LABEL_COLUMN] = marker.brain_target_list[i]["label"] + list_entry[const.BRAIN_MEP_COLUMN] = ( str(marker.brain_target_list[i]["mep_value"]) if marker.brain_target_list[i]["mep_value"] else "" ) - list_entry[const.UUID] = ( + list_entry[const.BRAIN_UUID] = ( str(marker.brain_target_list[i]["marker_uuid"]) if marker.brain_target_list[i]["marker_uuid"] else "" ) - self.sub_marker_list_ctrl.Append(list_entry) + self.brain_targets_list_ctrl.Append(list_entry) x, y, z = marker.brain_target_list[i]["position"] brain_targets.append({ "position": [x, -y, z], @@ -2668,8 +2686,9 @@ def OnMarkerFocused(self, evt): # e.g., shift and page-up/page-down keys. The bug is that the EVT_LIST_ITEM_SELECTED event # is triggered repeatedly for the same item (the one that was first selected). This is a # workaround to prevent the event from being triggered repeatedly for the same item. - if self.currently_focused_marker is not None and marker == self.currently_focused_marker: - return + # TODO: check here!! + # if self.currently_focused_marker is not None and marker == self.currently_focused_marker: + # return # When selecting multiple markers, e.g., by pressing ctrl while clicking on the markers, EVT_LIST_ITEM_SELECTED # event is triggered for each selected item, without triggering EVT_LIST_ITEM_DESELECTED event for the previously @@ -2683,12 +2702,12 @@ def OnMarkerFocused(self, evt): self.currently_focused_marker = marker self.markers.SelectMarker(marker_id) - self.sub_marker_list_ctrl.DeleteAllItems() + self.brain_targets_list_ctrl.DeleteAllItems() if marker.brain_target_list: self.populate_sub_list(marker.brain_target_list) - self.sub_marker_list_ctrl.Show() + self.brain_targets_list_ctrl.Show() else: - self.sub_marker_list_ctrl.Hide() + self.brain_targets_list_ctrl.Hide() self.Layout() # Refresh layout self.Update() @@ -2754,6 +2773,18 @@ def ChangeLabel(self, evt): ) self.markers.ChangeLabel(marker, new_label) + def ChangeLabelBrainTarget(self, evt): + list_index = self.brain_targets_list_ctrl.GetFocusedItem() + if list_index == -1: + wx.MessageBox(_("No data selected."), _("InVesalius 3")) + return + marker = self.currently_focused_marker.brain_target_list[list_index] + marker["label"] = dlg.ShowEnterMarkerID( + marker["label"] + ) + self.brain_targets_list_ctrl.SetItem(list_index, const.BRAIN_LABEL_COLUMN, marker["label"]) + self.markers.SaveState() + def OnMenuSetTarget(self, evt): idx = self.marker_list_ctrl.GetFocusedItem() if idx == -1: @@ -2985,6 +3016,7 @@ def OnCreateBrainTargetFromLandmark(self, evt): marker.y_mtms = np.round(mtms_coords[1], 1) marker.r_mtms = np.round(orientation[2], 0) marker_coil.brain_target_list.append(marker.to_brain_targets_dict()) + self.OnMarkerFocused(evt=None) self.markers.SaveState() dialog.Destroy() @@ -3018,6 +3050,19 @@ def OnMenuChangeMEP(self, evt): new_mep = dlg.ShowEnterMEPValue(self.marker_list_ctrl.GetItemText(idx, const.MEP_COLUMN)) self.markers.ChangeMEP(marker, new_mep) + def OnMenuChangeMEPBrainTarget(self, evt): + list_index = self.brain_targets_list_ctrl.GetFocusedItem() + if list_index == -1: + wx.MessageBox(_("No data selected."), _("InVesalius 3")) + return + marker = self.currently_focused_marker.brain_target_list[list_index] + if not marker["mep_value"]: + marker["mep_value"] = "0" + marker["mep_value"] = dlg.ShowEnterMEPValue( + marker["mep_value"] + ) + self.brain_targets_list_ctrl.SetItem(list_index, const.BRAIN_MEP_COLUMN, str(marker["mep_value"])) + def _UnsetTarget(self, marker): idx = self.__find_marker_index(marker.marker_id) @@ -3205,10 +3250,6 @@ def OnDeleteSelectedMarkers(self, evt=None): if result != wx.ID_OK: return - for index in indexes: - if index in self.sub_marker_items_list: - self.sub_marker_items_list.pop(index) - self.__delete_multiple_markers(indexes) # Re-focus on the marker with the same index as the first marker that was selected before deletion. @@ -3222,6 +3263,19 @@ def OnDeleteSelectedMarkers(self, evt=None): self.FocusOnMarker(first_existing_index) + def OnDeleteSelectedBrainTarget(self, evt): + list_index = self.brain_targets_list_ctrl.GetFocusedItem() + if list_index == -1: + wx.MessageBox(_("No data selected."), _("InVesalius 3")) + return + brain_target_list = self.currently_focused_marker.brain_target_list + target_uuid = self.brain_targets_list_ctrl.GetItemText(list_index, const.UUID) + # Remove entry with the specified UUID + markers = [marker for marker in brain_target_list if marker.get('marker_uuid') != target_uuid] + self.currently_focused_marker.brain_target_list = markers + self.OnMarkerFocused(evt=None) + self.markers.SaveState() + def GetNextMarkerLabel(self): return self.markers.GetNextMarkerLabel() @@ -3565,29 +3619,12 @@ def _AddMarker(self, marker, render, focus): list_entry.append(round(marker.y, 1)) list_entry.append(round(marker.z, 1)) - if marker.marker_type == MarkerType.BRAIN_TARGET: - focused_marker_idx = self.marker_list_ctrl.GetFocusedItem() - if focused_marker_idx >= 0: - num_items = focused_marker_idx - if focused_marker_idx in self.sub_marker_items_list: - key = ( - str(focused_marker_idx) - + "." - + str(len(self.sub_marker_items_list[focused_marker_idx])) - ) - list_entry[const.ID_COLUMN] = key - self.sub_marker_items_list[focused_marker_idx].append(list_entry.copy()) - else: - key = str(focused_marker_idx) + ".0" - list_entry[const.ID_COLUMN] = key - self.sub_marker_items_list[focused_marker_idx] = [list_entry.copy()] - else: - key = 0 - if len(self.itemDataMap) > 0: - # If itemDataMap is not empty, set the new key as last key + 1 - key = len(self.itemDataMap.keys()) + 1 - self.marker_list_ctrl.Append(list_entry) - self.marker_list_ctrl.SetItemData(num_items, key) + key = 0 + if len(self.itemDataMap) > 0: + # If itemDataMap is not empty, set the new key as last key + 1 + key = len(self.itemDataMap.keys()) + 1 + self.marker_list_ctrl.Append(list_entry) + self.marker_list_ctrl.SetItemData(num_items, key) data_map_entry = list_entry.copy() From 9feeb744817a5a828ddc6407582a4d89cead363f Mon Sep 17 00:00:00 2001 From: Renan Date: Sun, 3 Nov 2024 21:12:17 +0200 Subject: [PATCH 15/33] CLEAN --- .../data/visualization/marker_visualizer.py | 9 ----- invesalius/gui/task_navigator.py | 39 +++++-------------- invesalius/navigation/markers.py | 8 ---- 3 files changed, 9 insertions(+), 47 deletions(-) diff --git a/invesalius/data/visualization/marker_visualizer.py b/invesalius/data/visualization/marker_visualizer.py index 377370295..3fb4bc220 100644 --- a/invesalius/data/visualization/marker_visualizer.py +++ b/invesalius/data/visualization/marker_visualizer.py @@ -66,7 +66,6 @@ def __bind_events(self): Publisher.subscribe(self.HideMarkers, "Hide markers") Publisher.subscribe(self.ShowMarkers, "Show markers") Publisher.subscribe(self.DeleteMarkers, "Delete markers") - Publisher.subscribe(self.DeleteBrainMarkers, "Delete brain markers") Publisher.subscribe(self.DeleteMarker, "Delete marker") Publisher.subscribe(self.SetCameraToFocusOnMarker, "Set camera to focus on marker") Publisher.subscribe(self.HighlightMarker, "Highlight marker") @@ -245,14 +244,6 @@ def ShowMarkers(self, markers): if not self.is_navigating: self.interactor.Render() - def DeleteBrainMarkers(self, brain_markers): - for marker in brain_markers: - actor = marker.visualization["actor"] - self.renderer.RemoveActor(actor) - - if not self.is_navigating: - self.interactor.Render() - def DeleteMarkers(self, markers): for marker in markers: actor = marker.visualization["actor"] diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 2305d2834..e00e2de19 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2239,13 +2239,7 @@ def __get_selected_items(self): return selection def __delete_multiple_markers(self, indexes): - marker_ids = [] - for idx in indexes: - current_uuid = self.marker_list_ctrl.GetItem(idx, const.UUID).GetText() - for marker in self.markers.list: - if current_uuid == marker.marker_uuid: - marker_id = self.markers.list.index(marker) - marker_ids.append(marker_id) + marker_ids = [self.__get_marker_id(idx) for idx in indexes] self.markers.DeleteMultiple(marker_ids) def _DeleteMarker(self, marker): @@ -2288,16 +2282,8 @@ def _DeleteMultiple(self, markers): if current_uuid == deleted_uuid: deleted_keys.append(key) - current_uuid = marker.marker_uuid - for i in range(self.marker_list_ctrl.GetItemCount()): - if current_uuid == self.marker_list_ctrl.GetItem(i, const.UUID).GetText(): - idx = i - if idx: - self.marker_list_ctrl.DeleteItem(idx) - deleted_ids.append(marker.marker_id) - else: - self.marker_list_ctrl.DeleteItem(0) - deleted_ids.append(marker.marker_id) + self.marker_list_ctrl.DeleteItem(idx) + deleted_ids.append(marker.marker_id) # Remove all the deleted markers from itemDataMap for key in deleted_keys: @@ -2309,12 +2295,11 @@ def _DeleteMultiple(self, markers): num_items = self.marker_list_ctrl.GetItemCount() for n in range(num_items): m_id = self.__get_marker_id(n) - if m_id: - reduction_in_m_id = 0 - for d_id in deleted_ids: - if m_id > d_id: - reduction_in_m_id += 1 - self.marker_list_ctrl.SetItem(n, const.ID_COLUMN, str(m_id - reduction_in_m_id)) + reduction_in_m_id = 0 + for d_id in deleted_ids: + if m_id > d_id: + reduction_in_m_id += 1 + self.marker_list_ctrl.SetItem(n, const.ID_COLUMN, str(m_id - reduction_in_m_id)) self.marker_list_ctrl.Show() @@ -3107,12 +3092,6 @@ def __get_marker(self, idx): """ For an index in self.marker_list_ctrl, returns the corresponding marker """ - current_uuid = self.marker_list_ctrl.GetItem(idx, const.UUID).GetText() - for marker in self.markers.list: - if current_uuid == marker.marker_uuid: - marker_id = self.markers.list.index(marker) - return self.markers.list[marker_id] - marker_id = self.__get_marker_id(idx) return self.markers.list[marker_id] @@ -3269,7 +3248,7 @@ def OnDeleteSelectedBrainTarget(self, evt): wx.MessageBox(_("No data selected."), _("InVesalius 3")) return brain_target_list = self.currently_focused_marker.brain_target_list - target_uuid = self.brain_targets_list_ctrl.GetItemText(list_index, const.UUID) + target_uuid = self.brain_targets_list_ctrl.GetItemText(list_index, const.BRAIN_UUID) # Remove entry with the specified UUID markers = [marker for marker in brain_target_list if marker.get('marker_uuid') != target_uuid] self.currently_focused_marker.brain_target_list = markers diff --git a/invesalius/navigation/markers.py b/invesalius/navigation/markers.py index 62ba50102..e666096b8 100644 --- a/invesalius/navigation/markers.py +++ b/invesalius/navigation/markers.py @@ -119,18 +119,10 @@ def DeleteMarker(self, marker_id, render=True): def DeleteMultiple(self, marker_ids): markers = [] - brain_markers = [] for m_id in sorted(marker_ids, reverse=True): markers.append(self.list[m_id]) - if self.list[m_id].brain_target_list: - for m in self.list[m_id].brain_target_list: - for i in self.list: - if i.marker_uuid == m["marker_uuid"]: - brain_markers.append(self.list[i.marker_id]) self.DeleteMarker(m_id, render=False) - if brain_markers: - Publisher.sendMessage("Delete brain markers", brain_markers=brain_markers) Publisher.sendMessage("Delete markers", markers=markers) for idx, m in enumerate(self.list): From 34cf71a52adb5889d143021dd8220c35014181ac Mon Sep 17 00:00:00 2001 From: Renan Date: Sun, 3 Nov 2024 22:05:23 +0200 Subject: [PATCH 16/33] CLEAN --- invesalius/navigation/markers.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/invesalius/navigation/markers.py b/invesalius/navigation/markers.py index e666096b8..6db6b4075 100644 --- a/invesalius/navigation/markers.py +++ b/invesalius/navigation/markers.py @@ -90,23 +90,15 @@ def Clear(self): # currently not be used outside this class. def DeleteMarker(self, marker_id, render=True): marker = self.list[marker_id] - indices_to_delete = [marker_id] if marker.is_target: self.UnsetTarget(marker_id) if marker.is_point_of_interest: self.UnsetPointOfInterest(marker_id) - if marker.brain_target_list: - for m in marker.brain_target_list: - for i in self.list: - if i.marker_uuid == m["marker_uuid"]: - indices_to_delete.append(i.marker_id) if render: Publisher.sendMessage("Delete marker", marker=marker) - indices_to_delete.sort(reverse=True) - for i in indices_to_delete: - del self.list[i] + del self.list[marker_id] if render: for idx, m in enumerate(self.list): @@ -302,6 +294,3 @@ def CreateCoilTargetFromCoilPose(self, marker): new_marker.marker_type = MarkerType.COIL_TARGET self.AddMarker(new_marker) - - def AddBrainTarget(self, marker, new_brain_target): - marker.brain_target_list(new_brain_target) From f7dfb2c16336b208403f6654b4518ef9a3b1e664 Mon Sep 17 00:00:00 2001 From: Renan Date: Sun, 3 Nov 2024 22:05:41 +0200 Subject: [PATCH 17/33] ADD: color to marker with brain targets --- invesalius/gui/task_navigator.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index e00e2de19..f041be856 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -3001,6 +3001,9 @@ def OnCreateBrainTargetFromLandmark(self, evt): marker.y_mtms = np.round(mtms_coords[1], 1) marker.r_mtms = np.round(orientation[2], 0) marker_coil.brain_target_list.append(marker.to_brain_targets_dict()) + + if marker_coil.brain_target_list: + self.marker_list_ctrl.SetItemBackgroundColour(list_index, wx.Colour(102, 178, 255)) self.OnMarkerFocused(evt=None) self.markers.SaveState() dialog.Destroy() @@ -3611,6 +3614,9 @@ def _AddMarker(self, marker, render, focus): data_map_entry.append(marker.marker_uuid) self.itemDataMap[key] = data_map_entry + if marker.brain_target_list: + self.marker_list_ctrl.SetItemBackgroundColour(num_items, wx.Colour(102, 178, 255)) + self.marker_list_ctrl.EnsureVisible(num_items) # Focus on the added marker. From c58812c7e66c32c96cdfe0c553935c2f7cc3d8ba Mon Sep 17 00:00:00 2001 From: Renan Date: Sun, 3 Nov 2024 22:19:29 +0200 Subject: [PATCH 18/33] FIX: SetVisibility --- .../data/visualization/marker_visualizer.py | 22 ++++++++++++------- invesalius/gui/task_navigator.py | 2 ++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/invesalius/data/visualization/marker_visualizer.py b/invesalius/data/visualization/marker_visualizer.py index 3fb4bc220..9482c2a57 100644 --- a/invesalius/data/visualization/marker_visualizer.py +++ b/invesalius/data/visualization/marker_visualizer.py @@ -78,6 +78,7 @@ def __bind_events(self): Publisher.subscribe(self.UpdateBrainTargets, "Update brain targets") Publisher.subscribe(self.UpdateNavigationStatus, "Navigation status") Publisher.subscribe(self.UpdateTargetMode, "Set target mode") + Publisher.subscribe(self.UpdateVectorFieldAssemblyVisibility, "Set vector field assembly visibility") def UpdateNavigationStatus(self, nav_status, vis_status): self.is_navigating = nav_status @@ -85,13 +86,18 @@ def UpdateNavigationStatus(self, nav_status, vis_status): def UpdateTargetMode(self, enabled=False): self.is_target_mode = enabled + def UpdateVectorFieldAssemblyVisibility(self, enabled=False): + self.vector_field_assembly.SetVisibility(enabled) + # If not navigating, render the scene. + if not self.is_navigating: + self.interactor.Render() + def UpdateBrainTargets(self, brain_targets): """ Update the vector field assembly to reflect the current vector field. """ # Create a new vector field assembly. new_vector_field_assembly = self.vector_field_visualizer.CreateVectorFieldAssembly(brain_targets) - self.vector_field_assembly.SetVisibility(1) # Replace the old vector field assembly with the new one. self.actor_factory.ReplaceActor( self.renderer, self.vector_field_assembly, new_vector_field_assembly @@ -446,13 +452,13 @@ def HighlightMarker(self, marker, render=True): # If the marker is a coil target, show the vector field assembly and update its position and orientation, # otherwise, hide the vector field assembly. - if marker_type == MarkerType.COIL_TARGET: - self.vector_field_assembly.SetVisibility(1) - - self.vector_field_assembly.SetPosition(position_flipped) - self.vector_field_assembly.SetOrientation(orientation) - else: - self.vector_field_assembly.SetVisibility(0) + # if marker_type == MarkerType.COIL_TARGET: + # self.vector_field_assembly.SetVisibility(1) + # + # self.vector_field_assembly.SetPosition(position_flipped) + # self.vector_field_assembly.SetOrientation(orientation) + # else: + # self.vector_field_assembly.SetVisibility(0) # Return early if the marker is a target and the coil is at the target. # diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index f041be856..649b896b8 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2689,9 +2689,11 @@ def OnMarkerFocused(self, evt): self.markers.SelectMarker(marker_id) self.brain_targets_list_ctrl.DeleteAllItems() if marker.brain_target_list: + Publisher.sendMessage("Set vector field assembly visibility", enabled=True) self.populate_sub_list(marker.brain_target_list) self.brain_targets_list_ctrl.Show() else: + Publisher.sendMessage("Set vector field assembly visibility", enabled=False) self.brain_targets_list_ctrl.Hide() self.Layout() # Refresh layout self.Update() From 294636c39e5fc55212fce998eda46b6a58f5d551 Mon Sep 17 00:00:00 2001 From: Renan Date: Mon, 4 Nov 2024 19:13:28 +0200 Subject: [PATCH 19/33] ADD: receive from mtms --- .../data/visualization/coil_visualizer.py | 2 + invesalius/gui/task_navigator.py | 53 ++++++++++--------- invesalius/net/neuronavigation_api.py | 12 ++++- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/invesalius/data/visualization/coil_visualizer.py b/invesalius/data/visualization/coil_visualizer.py index 2535a9cdd..4145155c7 100644 --- a/invesalius/data/visualization/coil_visualizer.py +++ b/invesalius/data/visualization/coil_visualizer.py @@ -301,4 +301,6 @@ def UpdateCoilPoses(self, m_imgs, coords): self.coils[name]["center_actor"].SetUserMatrix(m_img_vtk) # LUKATODO + + ###LOOK HERE! # self.vector_field_assembly.SetUserMatrix(m_img_vtk) diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 649b896b8..5d9ee8035 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2222,7 +2222,8 @@ def __bind_events(self): Publisher.subscribe(self._UpdateMarkerLabel, "Update marker label") Publisher.subscribe(self._UpdateMEP, "Update marker mep") - Publisher.subscribe(self.SetVectorField, "Set vector field") + Publisher.subscribe(self.SetBrainTarget, "Set brain targets") + #Publisher.subscribe(self.SetVectorField, "Set vector field") def __get_selected_items(self): """ @@ -2381,32 +2382,32 @@ def UpdateSeedCoordinates( def UpdateCortexMarker(self, CoGposition, CoGorientation): self.cortex_position_orientation = CoGposition + CoGorientation - def SetVectorField(self, vector_field): - self.vector_field = vector_field + def SetBrainTarget(self, brain_targets): + marker_target = self.markers.FindTarget() + if not marker_target: + return - # TODO: Add this to new markers from mtms - # marker_target = self.markers.FindTarget() - # if not marker_target: - # return - # - # for vector in self.vector_field: - # position = vector["position"], - # orientation = vector["orientation"], - # colour = vector["color"], - # length_multiplier = vector["length"], - # - # marker = self.CreateMarker( - # position=position, - # orientation=orientation, - # colour=colour, - # size=length_multiplier, - # label=str(marker_target.label), - # marker_type=MarkerType.BRAIN_TARGET, - # ) - # marker.marker_uuid = str(uuid.uuid4()) - # marker_target.brain_target_list.append(marker.to_brain_targets_dict()) - # - # self.markers.SaveState() + for target in brain_targets: + position = target["position"], + orientation = target["orientation"], + colour = target["color"], + length_multiplier = target["length"], + + marker = self.CreateMarker( + position=position, + orientation=orientation, + colour=colour, + size=length_multiplier, + label=str(marker_target.label), + marker_type=MarkerType.BRAIN_TARGET, + ) + marker.marker_uuid = str(uuid.uuid4()) + marker.x_mtms = -target["position"][1] + marker.y_mtms = target["position"][0] + marker.r_mtms = -target["orientation"][2] + marker_target.brain_target_list.append(marker.to_brain_targets_dict()) + + self.markers.SaveState() def OnMouseRightDown(self, evt): focused_marker_idx = self.marker_list_ctrl.GetFocusedItem() diff --git a/invesalius/net/neuronavigation_api.py b/invesalius/net/neuronavigation_api.py index e0cd426db..753eba805 100644 --- a/invesalius/net/neuronavigation_api.py +++ b/invesalius/net/neuronavigation_api.py @@ -334,9 +334,17 @@ def remove_pedal_callback(self, name): def open_orientation_dialog(self, target_id): wx.CallAfter(Publisher.sendMessage, "Open marker orientation dialog", marker_id=target_id) - def stimulation_pulse_received(self): + def stimulation_pulse_received(self, targets): # TODO: If marker should not be created always when receiving a stimulation pulse, add the logic here. - wx.CallAfter(Publisher.sendMessage, "Create marker", marker_type=MarkerType.COIL_POSE) + brain_targets = [] + for target in targets: + brain_targets.append({ + "position": [target.displacement_y, -target.displacement_x, -15], + "orientation": [0, 0, -target.rotation_angle], + "color": [0, 0, 1], + "length": target.intensity / 100 + }) + wx.CallAfter(Publisher.sendMessage, "Set brain targets", brain_targets=brain_targets) def set_vector_field(self, vector_field): # Modify vector_field to swap x and y coordinates and adjust z orientation to match mTMS From e617038b4d646e3510abbcc0761c7c034fb5e273 Mon Sep 17 00:00:00 2001 From: rmatsuda Date: Wed, 6 Nov 2024 09:13:56 +0200 Subject: [PATCH 20/33] ADD: x,y,r mTMS in list ctrl --- invesalius/constants.py | 5 ++++- invesalius/gui/task_navigator.py | 14 +++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/invesalius/constants.py b/invesalius/constants.py index 75912ac82..2ea30ac4e 100644 --- a/invesalius/constants.py +++ b/invesalius/constants.py @@ -814,7 +814,10 @@ BRAIN_MARKER_TYPE_COLUMN = 2 BRAIN_LABEL_COLUMN = 3 BRAIN_MEP_COLUMN = 4 -BRAIN_UUID = 5 +BRAIN_X_MTMS = 5 +BRAIN_Y_MTMS = 6 +BRAIN_R_MTMS = 7 +BRAIN_UUID = 8 # ------------ Navigation defaults ------------------- diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 5d9ee8035..1427fa9d3 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2138,7 +2138,7 @@ def __init__(self, parent, nav_hub): # Sub List Control brain_targets_list_ctrl = wx.ListCtrl(self, style=wx.LC_REPORT) brain_targets_list_ctrl.InsertColumn(const.BRAIN_ID_COLUMN, "#") - brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_ID_COLUMN, 24) + brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_ID_COLUMN, 26) brain_targets_list_ctrl.InsertColumn(const.BRAIN_SESSION_COLUMN, "Session") brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_SESSION_COLUMN, 51) @@ -2152,6 +2152,15 @@ def __init__(self, parent, nav_hub): brain_targets_list_ctrl.InsertColumn(const.BRAIN_MEP_COLUMN, "MEP (uV)") brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_MEP_COLUMN, 45) + brain_targets_list_ctrl.InsertColumn(const.BRAIN_X_MTMS, "X") + brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_X_MTMS, 45) + + brain_targets_list_ctrl.InsertColumn(const.BRAIN_Y_MTMS, "Y") + brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_Y_MTMS, 45) + + brain_targets_list_ctrl.InsertColumn(const.BRAIN_R_MTMS, "R") + brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_R_MTMS, 45) + brain_targets_list_ctrl.InsertColumn(const.BRAIN_UUID, "UUID") brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_UUID, 45) # self.brain_targets_list_ctrl.Hide() @@ -2642,6 +2651,9 @@ def populate_sub_list(self, sub_items_list): if marker.brain_target_list[i]["mep_value"] else "" ) + list_entry[const.BRAIN_X_MTMS] = marker.brain_target_list[i]["x_mtms"] + list_entry[const.BRAIN_Y_MTMS] = marker.brain_target_list[i]["y_mtms"] + list_entry[const.BRAIN_R_MTMS] = marker.brain_target_list[i]["r_mtms"] list_entry[const.BRAIN_UUID] = ( str(marker.brain_target_list[i]["marker_uuid"]) if marker.brain_target_list[i]["marker_uuid"] From f18d5a31fde61cc6c61f96b84820645d29213448 Mon Sep 17 00:00:00 2001 From: rmatsuda Date: Wed, 6 Nov 2024 09:14:46 +0200 Subject: [PATCH 21/33] RUFF --- invesalius/gui/task_navigator.py | 83 +++++++++++++++++++------------- 1 file changed, 49 insertions(+), 34 deletions(-) diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 1427fa9d3..97754412b 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2165,7 +2165,9 @@ def __init__(self, parent, nav_hub): brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_UUID, 45) # self.brain_targets_list_ctrl.Hide() - brain_targets_list_ctrl.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnMouseRightDownBrainTargets) + brain_targets_list_ctrl.Bind( + wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnMouseRightDownBrainTargets + ) self.brain_targets_list_ctrl = brain_targets_list_ctrl # In the future, it would be better if the panel could initialize itself based on markers in MarkersControl try: @@ -2232,7 +2234,7 @@ def __bind_events(self): Publisher.subscribe(self._UpdateMEP, "Update marker mep") Publisher.subscribe(self.SetBrainTarget, "Set brain targets") - #Publisher.subscribe(self.SetVectorField, "Set vector field") + # Publisher.subscribe(self.SetVectorField, "Set vector field") def __get_selected_items(self): """ @@ -2397,10 +2399,10 @@ def SetBrainTarget(self, brain_targets): return for target in brain_targets: - position = target["position"], - orientation = target["orientation"], - colour = target["color"], - length_multiplier = target["length"], + position = (target["position"],) + orientation = (target["orientation"],) + colour = (target["color"],) + length_multiplier = (target["length"],) marker = self.CreateMarker( position=position, @@ -2587,7 +2589,6 @@ def OnMouseRightDownBrainTargets(self, evt): mep_menu_item = menu_id.Append(unique_menu_id + 3, _("Change MEP value")) menu_id.Bind(wx.EVT_MENU, self.OnMenuChangeMEPBrainTarget, mep_menu_item) - if has_mTMS: send_brain_target_menu_item = menu_id.Append( unique_menu_id + 5, _("Send brain target to mTMS") @@ -2641,7 +2642,7 @@ def populate_sub_list(self, sub_items_list): num_items = focused_marker_idx brain_targets = [] for i, sub_item in enumerate(sub_items_list): - list_entry = ["" for _ in range(0, const.BRAIN_UUID+1)] + list_entry = ["" for _ in range(0, const.BRAIN_UUID + 1)] list_entry[const.BRAIN_ID_COLUMN] = str(num_items) + "." + str(i) list_entry[const.BRAIN_SESSION_COLUMN] = str(marker.brain_target_list[i]["session_id"]) list_entry[const.BRAIN_MARKER_TYPE_COLUMN] = MarkerType.BRAIN_TARGET.human_readable @@ -2661,12 +2662,14 @@ def populate_sub_list(self, sub_items_list): ) self.brain_targets_list_ctrl.Append(list_entry) x, y, z = marker.brain_target_list[i]["position"] - brain_targets.append({ - "position": [x, -y, z], - "orientation": marker.brain_target_list[i]["orientation"], - "color": marker.brain_target_list[i]["colour"], - "length": marker.brain_target_list[i]["size"] - }) + brain_targets.append( + { + "position": [x, -y, z], + "orientation": marker.brain_target_list[i]["orientation"], + "color": marker.brain_target_list[i]["colour"], + "length": marker.brain_target_list[i]["size"], + } + ) Publisher.sendMessage("Update brain targets", brain_targets=brain_targets) # Called when a marker on the list gets the focus by the user left-clicking on it. @@ -2779,9 +2782,7 @@ def ChangeLabelBrainTarget(self, evt): wx.MessageBox(_("No data selected."), _("InVesalius 3")) return marker = self.currently_focused_marker.brain_target_list[list_index] - marker["label"] = dlg.ShowEnterMarkerID( - marker["label"] - ) + marker["label"] = dlg.ShowEnterMarkerID(marker["label"]) self.brain_targets_list_ctrl.SetItem(list_index, const.BRAIN_LABEL_COLUMN, marker["label"]) self.markers.SaveState() @@ -2953,12 +2954,15 @@ def transform_to_mtms(self, coil_position, coil_orientation_euler, brain_positio The brain position in the coil's coordinate system. """ import invesalius.data.transformations as tr + # Convert inputs to numpy arrays coil_position = np.array(coil_position) brain_position = np.array(brain_position) # Convert Euler angles to rotation matrix - coil_rotation_matrix = tr.euler_matrix(coil_orientation_euler[0], coil_orientation_euler[1], coil_orientation_euler[2], 'sxyz') + coil_rotation_matrix = tr.euler_matrix( + coil_orientation_euler[0], coil_orientation_euler[1], coil_orientation_euler[2], "sxyz" + ) # Step 1: Translate brain position to the coil's origin translated_position = brain_position - coil_position @@ -2995,11 +2999,11 @@ def OnCreateBrainTargetFromLandmark(self, evt): label=str(marker_coil.label), ) marker.marker_uuid = str(uuid.uuid4()) - #EXAMPLE. TODO with mtms + # EXAMPLE. TODO with mtms mtms_coords = self.transform_to_mtms(position_coil, orientation, position) - marker.x_mtms = np.round(mtms_coords[0],1) - marker.y_mtms = np.round(mtms_coords[1],1) - marker.r_mtms = np.round(orientation[2],0) + marker.x_mtms = np.round(mtms_coords[0], 1) + marker.y_mtms = np.round(mtms_coords[1], 1) + marker.r_mtms = np.round(orientation[2], 0) marker_coil.brain_target_list.append(marker.to_brain_targets_dict()) for position, orientation in zip(brain_position_list, brain_orientation_list): @@ -3061,10 +3065,10 @@ def OnMenuChangeMEPBrainTarget(self, evt): marker = self.currently_focused_marker.brain_target_list[list_index] if not marker["mep_value"]: marker["mep_value"] = "0" - marker["mep_value"] = dlg.ShowEnterMEPValue( - marker["mep_value"] + marker["mep_value"] = dlg.ShowEnterMEPValue(marker["mep_value"]) + self.brain_targets_list_ctrl.SetItem( + list_index, const.BRAIN_MEP_COLUMN, str(marker["mep_value"]) ) - self.brain_targets_list_ctrl.SetItem(list_index, const.BRAIN_MEP_COLUMN, str(marker["mep_value"])) def _UnsetTarget(self, marker): idx = self.__find_marker_index(marker.marker_id) @@ -3268,7 +3272,9 @@ def OnDeleteSelectedBrainTarget(self, evt): brain_target_list = self.currently_focused_marker.brain_target_list target_uuid = self.brain_targets_list_ctrl.GetItemText(list_index, const.BRAIN_UUID) # Remove entry with the specified UUID - markers = [marker for marker in brain_target_list if marker.get('marker_uuid') != target_uuid] + markers = [ + marker for marker in brain_target_list if marker.get("marker_uuid") != target_uuid + ] self.currently_focused_marker.brain_target_list = markers self.OnMarkerFocused(evt=None) self.markers.SaveState() @@ -3352,24 +3358,33 @@ def ParseValue(self, value): # Try to convert to int or float try: - if '.' in value or 'e' in value.lower(): + if "." in value or "e" in value.lower(): return float(value) return int(value) except ValueError: # Handle quoted strings - if (value.startswith('"') and value.endswith('"')) or (value.startswith("'") and value.endswith("'")): + if (value.startswith('"') and value.endswith('"')) or ( + value.startswith("'") and value.endswith("'") + ): return value[1:-1] return value # Return as is if not recognized def _parse_list(self, list_str): """Parse a list from string format.""" - return [self.ParseValue(el.strip()) for el in self._split_by_outer_commas(list_str[1:-1].strip())] + return [ + self.ParseValue(el.strip()) + for el in self._split_by_outer_commas(list_str[1:-1].strip()) + ] def _parse_dict(self, dict_str): """Parse a dictionary from string format.""" items = self._split_by_outer_commas(dict_str[1:-1].strip()) - return {self.ParseValue(kv.split(":", 1)[0].strip()): self.ParseValue(kv.split(":", 1)[1].strip()) for kv in - items} + return { + self.ParseValue(kv.split(":", 1)[0].strip()): self.ParseValue( + kv.split(":", 1)[1].strip() + ) + for kv in items + } def _split_by_outer_commas(self, string): """Split a string by commas that are not inside brackets or braces.""" @@ -3383,14 +3398,14 @@ def _split_by_outer_commas(self, string): elif char in "]}" and depth > 0: depth -= 1 - if char == ',' and depth == 0: - elements.append(''.join(current_element).strip()) + if char == "," and depth == 0: + elements.append("".join(current_element).strip()) current_element = [] else: current_element.append(char) if current_element: - elements.append(''.join(current_element).strip()) + elements.append("".join(current_element).strip()) return elements From 8809a406d632e2b9ff1c9e37634c35ef6cecbfb0 Mon Sep 17 00:00:00 2001 From: rmatsuda Date: Wed, 6 Nov 2024 14:54:32 +0200 Subject: [PATCH 22/33] ADD: transform mtms coord to target coord system --- invesalius/constants.py | 3 +- invesalius/data/markers/marker.py | 1 + .../data/visualization/coil_visualizer.py | 1 - invesalius/gui/task_navigator.py | 58 ++++++++++++++----- invesalius/net/neuronavigation_api.py | 3 +- 5 files changed, 48 insertions(+), 18 deletions(-) diff --git a/invesalius/constants.py b/invesalius/constants.py index 2ea30ac4e..aa68543f6 100644 --- a/invesalius/constants.py +++ b/invesalius/constants.py @@ -817,7 +817,8 @@ BRAIN_X_MTMS = 5 BRAIN_Y_MTMS = 6 BRAIN_R_MTMS = 7 -BRAIN_UUID = 8 +BRAIN_INTENSITY_MTMS = 8 +BRAIN_UUID = 9 # ------------ Navigation defaults ------------------- diff --git a/invesalius/data/markers/marker.py b/invesalius/data/markers/marker.py index d811156db..63dfef2d6 100644 --- a/invesalius/data/markers/marker.py +++ b/invesalius/data/markers/marker.py @@ -225,6 +225,7 @@ def to_brain_targets_dict(self): "x_mtms": self.x_mtms, "y_mtms": self.y_mtms, "r_mtms": self.r_mtms, + "intensity_mtms": self.intensity_mtms, } def to_dict(self): diff --git a/invesalius/data/visualization/coil_visualizer.py b/invesalius/data/visualization/coil_visualizer.py index 4145155c7..fbc3d696b 100644 --- a/invesalius/data/visualization/coil_visualizer.py +++ b/invesalius/data/visualization/coil_visualizer.py @@ -302,5 +302,4 @@ def UpdateCoilPoses(self, m_imgs, coords): # LUKATODO - ###LOOK HERE! # self.vector_field_assembly.SetUserMatrix(m_img_vtk) diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 97754412b..1906ff15f 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -43,6 +43,7 @@ from wx.lib.mixins.listctrl import ColumnSorterMixin import invesalius.constants as const +import invesalius.data.coordinates as dco import invesalius.gui.dialogs as dlg import invesalius.project as prj import invesalius.session as ses @@ -2152,15 +2153,18 @@ def __init__(self, parent, nav_hub): brain_targets_list_ctrl.InsertColumn(const.BRAIN_MEP_COLUMN, "MEP (uV)") brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_MEP_COLUMN, 45) - brain_targets_list_ctrl.InsertColumn(const.BRAIN_X_MTMS, "X") + brain_targets_list_ctrl.InsertColumn(const.BRAIN_X_MTMS, "X (mm)") brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_X_MTMS, 45) - brain_targets_list_ctrl.InsertColumn(const.BRAIN_Y_MTMS, "Y") + brain_targets_list_ctrl.InsertColumn(const.BRAIN_Y_MTMS, "Y (mm)") brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_Y_MTMS, 45) - brain_targets_list_ctrl.InsertColumn(const.BRAIN_R_MTMS, "R") + brain_targets_list_ctrl.InsertColumn(const.BRAIN_R_MTMS, "R (°)") brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_R_MTMS, 45) + brain_targets_list_ctrl.InsertColumn(const.BRAIN_INTENSITY_MTMS, "Int. (V/m)") + brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_INTENSITY_MTMS, 45) + brain_targets_list_ctrl.InsertColumn(const.BRAIN_UUID, "UUID") brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_UUID, 45) # self.brain_targets_list_ctrl.Hide() @@ -2398,24 +2402,39 @@ def SetBrainTarget(self, brain_targets): if not marker_target: return - for target in brain_targets: - position = (target["position"],) - orientation = (target["orientation"],) - colour = (target["color"],) - length_multiplier = (target["length"],) + position = marker_target.position + orientation = marker_target.orientation + position[1] = -position[1] + m_marker_target = dco.coordinates_to_transformation_matrix( + position=position, + orientation=orientation, + axes="sxyz", + ) + for target in brain_targets: + m_offset_brain = dco.coordinates_to_transformation_matrix( + position=target["position"], + orientation=target["orientation"], + axes="sxyz", + ) + m_brain = m_marker_target @ m_offset_brain + new_position, new_orientation = dco.transformation_matrix_to_coordinates( + m_brain, "sxyz" + ) + new_position[1] = -new_position[1] marker = self.CreateMarker( - position=position, - orientation=orientation, - colour=colour, - size=length_multiplier, + position=new_position.tolist(), + orientation=new_orientation.tolist(), + colour=target["color"], + size=target["length"], label=str(marker_target.label), marker_type=MarkerType.BRAIN_TARGET, ) marker.marker_uuid = str(uuid.uuid4()) - marker.x_mtms = -target["position"][1] - marker.y_mtms = target["position"][0] - marker.r_mtms = -target["orientation"][2] + marker.x_mtms = target["mtms"][0] + marker.y_mtms = target["mtms"][1] + marker.r_mtms = target["mtms"][2] + marker.intensity_mtms = target["mtms"][3] marker_target.brain_target_list.append(marker.to_brain_targets_dict()) self.markers.SaveState() @@ -2655,6 +2674,7 @@ def populate_sub_list(self, sub_items_list): list_entry[const.BRAIN_X_MTMS] = marker.brain_target_list[i]["x_mtms"] list_entry[const.BRAIN_Y_MTMS] = marker.brain_target_list[i]["y_mtms"] list_entry[const.BRAIN_R_MTMS] = marker.brain_target_list[i]["r_mtms"] + list_entry[const.BRAIN_INTENSITY_MTMS] = marker.brain_target_list[i]["intensity_mtms"] list_entry[const.BRAIN_UUID] = ( str(marker.brain_target_list[i]["marker_uuid"]) if marker.brain_target_list[i]["marker_uuid"] @@ -2775,6 +2795,14 @@ def ChangeLabel(self, evt): self.marker_list_ctrl.GetItemText(list_index, const.LABEL_COLUMN) ) self.markers.ChangeLabel(marker, new_label) + brain_targets = [{ + "position": [10, -5, -15], + "orientation": [0, 0, -45], + "color": [0, 0, 1], + "length": 50 / 100, + "mtms": [5, 10, 45, 50], + }] + Publisher.sendMessage("Set brain targets", brain_targets=brain_targets) def ChangeLabelBrainTarget(self, evt): list_index = self.brain_targets_list_ctrl.GetFocusedItem() diff --git a/invesalius/net/neuronavigation_api.py b/invesalius/net/neuronavigation_api.py index 753eba805..699555f4b 100644 --- a/invesalius/net/neuronavigation_api.py +++ b/invesalius/net/neuronavigation_api.py @@ -342,7 +342,8 @@ def stimulation_pulse_received(self, targets): "position": [target.displacement_y, -target.displacement_x, -15], "orientation": [0, 0, -target.rotation_angle], "color": [0, 0, 1], - "length": target.intensity / 100 + "length": target.intensity / 100, + "mtms": [target.displacement_x, target.displacement_y, target.rotation_angle, target.intensity], }) wx.CallAfter(Publisher.sendMessage, "Set brain targets", brain_targets=brain_targets) From ceb087eea939b2af2d2194abde8b01d945af33de Mon Sep 17 00:00:00 2001 From: rmatsuda Date: Wed, 6 Nov 2024 14:54:56 +0200 Subject: [PATCH 23/33] RUFF --- invesalius/gui/task_navigator.py | 16 +++++++++------- invesalius/net/neuronavigation_api.py | 23 +++++++++++++++-------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 1906ff15f..d88b70d29 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2795,13 +2795,15 @@ def ChangeLabel(self, evt): self.marker_list_ctrl.GetItemText(list_index, const.LABEL_COLUMN) ) self.markers.ChangeLabel(marker, new_label) - brain_targets = [{ - "position": [10, -5, -15], - "orientation": [0, 0, -45], - "color": [0, 0, 1], - "length": 50 / 100, - "mtms": [5, 10, 45, 50], - }] + brain_targets = [ + { + "position": [10, -5, -15], + "orientation": [0, 0, -45], + "color": [0, 0, 1], + "length": 50 / 100, + "mtms": [5, 10, 45, 50], + } + ] Publisher.sendMessage("Set brain targets", brain_targets=brain_targets) def ChangeLabelBrainTarget(self, evt): diff --git a/invesalius/net/neuronavigation_api.py b/invesalius/net/neuronavigation_api.py index 699555f4b..77d7fc68e 100644 --- a/invesalius/net/neuronavigation_api.py +++ b/invesalius/net/neuronavigation_api.py @@ -338,14 +338,21 @@ def stimulation_pulse_received(self, targets): # TODO: If marker should not be created always when receiving a stimulation pulse, add the logic here. brain_targets = [] for target in targets: - brain_targets.append({ - "position": [target.displacement_y, -target.displacement_x, -15], - "orientation": [0, 0, -target.rotation_angle], - "color": [0, 0, 1], - "length": target.intensity / 100, - "mtms": [target.displacement_x, target.displacement_y, target.rotation_angle, target.intensity], - }) - wx.CallAfter(Publisher.sendMessage, "Set brain targets", brain_targets=brain_targets) + brain_targets.append( + { + "position": [target.displacement_y, -target.displacement_x, -15], + "orientation": [0, 0, -target.rotation_angle], + "color": [0, 0, 1], + "length": target.intensity / 100, + "mtms": [ + target.displacement_x, + target.displacement_y, + target.rotation_angle, + target.intensity, + ], + } + ) + wx.CallAfter(Publisher.sendMessage, "Set brain targets", brain_targets=brain_targets) def set_vector_field(self, vector_field): # Modify vector_field to swap x and y coordinates and adjust z orientation to match mTMS From 3cc7654ff1e74be92c85007b34e49c79aa3cd1ff Mon Sep 17 00:00:00 2001 From: rmatsuda Date: Thu, 7 Nov 2024 14:54:48 +0200 Subject: [PATCH 24/33] ADD: enable vector_field for coil and create Coil_Pose markers for each stimulation --- invesalius/data/visualization/coil_visualizer.py | 6 +++--- invesalius/net/neuronavigation_api.py | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/invesalius/data/visualization/coil_visualizer.py b/invesalius/data/visualization/coil_visualizer.py index fbc3d696b..e85efd4e7 100644 --- a/invesalius/data/visualization/coil_visualizer.py +++ b/invesalius/data/visualization/coil_visualizer.py @@ -139,7 +139,7 @@ def ShowCoil(self, state, coil_name=None): if self.target_coil_actor is not None: self.target_coil_actor.SetVisibility(state) - # self.vector_field_assembly.SetVisibility(state) # LUKATODO: Keep this hidden for now + self.vector_field_assembly.SetVisibility(state) # LUKATODO: Keep this hidden for now if not self.is_navigating: Publisher.sendMessage("Render volume viewer") @@ -262,7 +262,7 @@ def AddCoil(self, coil_name, coil_path): self.coils[coil_name]["path"] = coil_path # LUKATODO: Vector field assembly follows a different pattern for addition, should unify. - # self.vector_field_assembly.SetVisibility(1) + self.vector_field_assembly.SetVisibility(1) def RemoveCoil(self, coil_name=None): if coil_name is not None: @@ -302,4 +302,4 @@ def UpdateCoilPoses(self, m_imgs, coords): # LUKATODO - # self.vector_field_assembly.SetUserMatrix(m_img_vtk) + self.vector_field_assembly.SetUserMatrix(m_img_vtk) diff --git a/invesalius/net/neuronavigation_api.py b/invesalius/net/neuronavigation_api.py index 77d7fc68e..3ee57818b 100644 --- a/invesalius/net/neuronavigation_api.py +++ b/invesalius/net/neuronavigation_api.py @@ -353,6 +353,7 @@ def stimulation_pulse_received(self, targets): } ) wx.CallAfter(Publisher.sendMessage, "Set brain targets", brain_targets=brain_targets) + wx.CallAfter(Publisher.sendMessage, "Create marker", marker_type=MarkerType.COIL_POSE) def set_vector_field(self, vector_field): # Modify vector_field to swap x and y coordinates and adjust z orientation to match mTMS From 92b24e726651b8d80e07f548ebeb5b32897254ee Mon Sep 17 00:00:00 2001 From: rmatsuda Date: Fri, 8 Nov 2024 11:25:39 +0200 Subject: [PATCH 25/33] ADD: CreateCoilTarget based on BrainTarget --- invesalius/data/markers/marker.py | 6 ++--- .../data/visualization/coil_visualizer.py | 2 +- invesalius/gui/task_navigator.py | 13 ++++++++++ invesalius/navigation/markers.py | 26 +++++++++++++++++++ 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/invesalius/data/markers/marker.py b/invesalius/data/markers/marker.py index 63dfef2d6..9328658fa 100644 --- a/invesalius/data/markers/marker.py +++ b/invesalius/data/markers/marker.py @@ -64,9 +64,9 @@ class Marker: is_target: bool = False is_point_of_interest: bool = False session_id: int = 1 - x_cortex: float = 0 - y_cortex: float = 0 - z_cortex: float = 0 + x_cortex: float = dataclasses.field(default=None) + y_cortex: float = dataclasses.field(default=None) + z_cortex: float = dataclasses.field(default=None) alpha_cortex: float = dataclasses.field(default=None) beta_cortex: float = dataclasses.field(default=None) gamma_cortex: float = dataclasses.field(default=None) diff --git a/invesalius/data/visualization/coil_visualizer.py b/invesalius/data/visualization/coil_visualizer.py index e85efd4e7..db19257eb 100644 --- a/invesalius/data/visualization/coil_visualizer.py +++ b/invesalius/data/visualization/coil_visualizer.py @@ -139,7 +139,7 @@ def ShowCoil(self, state, coil_name=None): if self.target_coil_actor is not None: self.target_coil_actor.SetVisibility(state) - self.vector_field_assembly.SetVisibility(state) # LUKATODO: Keep this hidden for now + self.vector_field_assembly.SetVisibility(state) # LUKATODO: Keep this hidden for now if not self.is_navigating: Publisher.sendMessage("Render volume viewer") diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index d88b70d29..61e8f1d0c 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2589,6 +2589,7 @@ def OnMouseRightDown(self, evt): def OnMouseRightDownBrainTargets(self, evt): focused_marker_idx = self.brain_targets_list_ctrl.GetFocusedItem() focused_marker = self.currently_focused_marker.brain_target_list[focused_marker_idx] + self.focused_brain_marker = focused_marker unique_menu_id = 1 # Check if the currently focused marker is the active target. @@ -2608,6 +2609,13 @@ def OnMouseRightDownBrainTargets(self, evt): mep_menu_item = menu_id.Append(unique_menu_id + 3, _("Change MEP value")) menu_id.Bind(wx.EVT_MENU, self.OnMenuChangeMEPBrainTarget, mep_menu_item) + create_coil_target_menu_item = menu_id.Append( + unique_menu_id + 4, _("Create coil target") + ) + menu_id.Bind( + wx.EVT_MENU, self.OnCreateCoilTargetFromBrainTargets, create_coil_target_menu_item + ) + if has_mTMS: send_brain_target_menu_item = menu_id.Append( unique_menu_id + 5, _("Send brain target to mTMS") @@ -2754,6 +2762,9 @@ def OnCreateCoilTargetFromLandmark(self, evt): self.markers.CreateCoilTargetFromLandmark(marker) + def OnCreateCoilTargetFromBrainTargets(self, evt): + self.markers.CreateCoilTargetFromBrainTarget(self.focused_brain_marker) + def OnCreateCoilTargetFromCoilPose(self, evt): list_index = self.marker_list_ctrl.GetFocusedItem() if list_index == -1: @@ -3034,6 +3045,7 @@ def OnCreateBrainTargetFromLandmark(self, evt): marker.x_mtms = np.round(mtms_coords[0], 1) marker.y_mtms = np.round(mtms_coords[1], 1) marker.r_mtms = np.round(orientation[2], 0) + marker.intensity_mtms = 10 marker_coil.brain_target_list.append(marker.to_brain_targets_dict()) for position, orientation in zip(brain_position_list, brain_orientation_list): @@ -3049,6 +3061,7 @@ def OnCreateBrainTargetFromLandmark(self, evt): marker.x_mtms = np.round(mtms_coords[0], 1) marker.y_mtms = np.round(mtms_coords[1], 1) marker.r_mtms = np.round(orientation[2], 0) + marker.intensity_mtms = 10 marker_coil.brain_target_list.append(marker.to_brain_targets_dict()) if marker_coil.brain_target_list: diff --git a/invesalius/navigation/markers.py b/invesalius/navigation/markers.py index 6db6b4075..6f6a7504b 100644 --- a/invesalius/navigation/markers.py +++ b/invesalius/navigation/markers.py @@ -288,6 +288,32 @@ def CreateCoilTargetFromLandmark(self, marker): self.AddMarker(new_marker) + def CreateCoilTargetFromBrainTarget(self, marker): + new_marker = Marker() + + new_marker.position = marker["position"] + new_marker.orientation = marker["orientation"] + new_marker.marker_type = MarkerType.COIL_TARGET + + # Marker IDs start from zero, hence len(self.markers) will be the ID of the new marker. + new_marker.marker_id = len(self.list) + # Create an uuid for the marker + new_marker.marker_uuid = str(uuid.uuid4()) + # Set the visualization attribute to an empty dictionary. + new_marker.visualization = {} + # Unset the is_target attribute. + new_marker.is_target = False + + self.transformator.ProjectToScalp( + marker=new_marker, + # We are projecting the marker that is on the brain surface; hence, project to the opposite side + # of the scalp because the normal vectors are unreliable on the brain side of the scalp. + opposite_side=True, + ) + new_marker.label = self.GetNextMarkerLabel() + + self.AddMarker(new_marker) + def CreateCoilTargetFromCoilPose(self, marker): new_marker = marker.duplicate() From 094b2abbe6e763b24d01abf37f3c9dd405a59dc7 Mon Sep 17 00:00:00 2001 From: rmatsuda Date: Fri, 8 Nov 2024 11:29:11 +0200 Subject: [PATCH 26/33] RUFF --- invesalius/gui/task_navigator.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 61e8f1d0c..da9b584c8 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2609,9 +2609,7 @@ def OnMouseRightDownBrainTargets(self, evt): mep_menu_item = menu_id.Append(unique_menu_id + 3, _("Change MEP value")) menu_id.Bind(wx.EVT_MENU, self.OnMenuChangeMEPBrainTarget, mep_menu_item) - create_coil_target_menu_item = menu_id.Append( - unique_menu_id + 4, _("Create coil target") - ) + create_coil_target_menu_item = menu_id.Append(unique_menu_id + 4, _("Create coil target")) menu_id.Bind( wx.EVT_MENU, self.OnCreateCoilTargetFromBrainTargets, create_coil_target_menu_item ) From ae3f30a73bd9f839662d3ef1fff882b15c71e962 Mon Sep 17 00:00:00 2001 From: rmatsuda Date: Fri, 8 Nov 2024 13:03:57 +0200 Subject: [PATCH 27/33] FIX: close project delete brain targets --- invesalius/gui/task_navigator.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index da9b584c8..7085a89ae 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -3267,6 +3267,9 @@ def OnDeleteAllMarkers(self, evt=None): return self.markers.Clear() self.itemDataMap.clear() + Publisher.sendMessage("Set vector field assembly visibility", enabled=False) + self.brain_targets_list_ctrl.DeleteAllItems() + self.brain_targets_list_ctrl.Hide() def OnDeleteFiducialMarker(self, label): indexes = [] From 150176e1704a1aaec55f5131ef12e3a4daf3c0c2 Mon Sep 17 00:00:00 2001 From: rmatsuda Date: Fri, 8 Nov 2024 15:51:10 +0200 Subject: [PATCH 28/33] FIX: enable motor mapping for brain targets --- .../data/visualization/mep_visualizer.py | 44 +++++++++++++++++++ invesalius/gui/task_navigator.py | 16 +++---- 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/invesalius/data/visualization/mep_visualizer.py b/invesalius/data/visualization/mep_visualizer.py index fad5abcfe..bec714c5e 100644 --- a/invesalius/data/visualization/mep_visualizer.py +++ b/invesalius/data/visualization/mep_visualizer.py @@ -84,6 +84,7 @@ def __bind_events(self): # Publisher.subscribe(self.UpdateMEPPoints, "Update marker list") # Publisher.subscribe(self.SetBrainSurface, "Set MEP brain surface") Publisher.subscribe(self.UpdateMEPPoints, "Redraw MEP mapping") + Publisher.subscribe(self.UpdateMEPPointsFromBrainTargets, "Redraw MEP mapping from brain targets") Publisher.subscribe(self.UpdateNavigationStatus, "Navigation status") Publisher.subscribe(self.SetBrainSurface, "Load brain surface actor") Publisher.subscribe(self.OnCloseProject, "Close project data") @@ -131,6 +132,7 @@ def DisplayMotorMap(self, show: bool): self.UpdateVisualization() progress_dialog.Update(value=50, msg="Preparing brain surface...") self.UpdateMEPPoints() + self.UpdateMEPPointsFromBrainTargets() progress_dialog.Close() else: self._config_params["mep_enabled"] = False @@ -353,6 +355,48 @@ def UpdateMEPPoints(self): if self._config_params["mep_enabled"]: self.UpdateVisualization() + def UpdateMEPPointsFromBrainTargets(self): + """ + Updates or creates the point data with MEP values from a list of markers. + + Args: + markers (List[Marker]): The list of marker objects to add/update points for. + clear_old (bool, default=False): If True, clears all existing points before updating. + """ + if not self._config_params["mep_enabled"]: + return + if not self.surface: + return + brain_markers = [] + for marker in MarkersControl().list: + if marker.brain_target_list: + for m in marker.brain_target_list: + brain_markers.append(m) + + if not brain_markers: + self.points = vtkPolyData() + self.UpdateVisualization() + return + + points = vtkPoints() + + point_data = self.points.GetPointData() + mep_array = vtkDoubleArray() + mep_array.SetName("MEP") + point_data.AddArray(mep_array) + + for marker in brain_markers: + points.InsertNextPoint(marker["position"][0], -marker["position"][1], marker["position"][2]) + mep_value = marker["mep_value"] or 0 + mep_array.InsertNextValue(mep_value) + MarkersControl().SaveState() + + self.points.SetPoints(points) + self.points.GetPointData().SetActiveScalars("MEP") + self.points.Modified() + if self._config_params["mep_enabled"]: + self.UpdateVisualization() + def UpdateVisualization(self): if not self._config_params["mep_enabled"] or not self.surface: return diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 7085a89ae..832e2ca99 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2435,8 +2435,11 @@ def SetBrainTarget(self, brain_targets): marker.y_mtms = target["mtms"][1] marker.r_mtms = target["mtms"][2] marker.intensity_mtms = target["mtms"][3] + #TODO: MEP + marker.mep_value = 0 marker_target.brain_target_list.append(marker.to_brain_targets_dict()) + Publisher.sendMessage("Redraw MEP mapping from brain targets") self.markers.SaveState() def OnMouseRightDown(self, evt): @@ -2804,16 +2807,6 @@ def ChangeLabel(self, evt): self.marker_list_ctrl.GetItemText(list_index, const.LABEL_COLUMN) ) self.markers.ChangeLabel(marker, new_label) - brain_targets = [ - { - "position": [10, -5, -15], - "orientation": [0, 0, -45], - "color": [0, 0, 1], - "length": 50 / 100, - "mtms": [5, 10, 45, 50], - } - ] - Publisher.sendMessage("Set brain targets", brain_targets=brain_targets) def ChangeLabelBrainTarget(self, evt): list_index = self.brain_targets_list_ctrl.GetFocusedItem() @@ -3106,10 +3099,11 @@ def OnMenuChangeMEPBrainTarget(self, evt): marker = self.currently_focused_marker.brain_target_list[list_index] if not marker["mep_value"]: marker["mep_value"] = "0" - marker["mep_value"] = dlg.ShowEnterMEPValue(marker["mep_value"]) + marker["mep_value"] = dlg.ShowEnterMEPValue(str(marker["mep_value"])) self.brain_targets_list_ctrl.SetItem( list_index, const.BRAIN_MEP_COLUMN, str(marker["mep_value"]) ) + Publisher.sendMessage("Redraw MEP mapping from brain targets") def _UnsetTarget(self, marker): idx = self.__find_marker_index(marker.marker_id) From 31f165925941f893579c25ba48378eff399aea0e Mon Sep 17 00:00:00 2001 From: rmatsuda Date: Fri, 8 Nov 2024 15:51:25 +0200 Subject: [PATCH 29/33] RUFF --- invesalius/data/visualization/mep_visualizer.py | 8 ++++++-- invesalius/gui/task_navigator.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/invesalius/data/visualization/mep_visualizer.py b/invesalius/data/visualization/mep_visualizer.py index bec714c5e..841a0f9d6 100644 --- a/invesalius/data/visualization/mep_visualizer.py +++ b/invesalius/data/visualization/mep_visualizer.py @@ -84,7 +84,9 @@ def __bind_events(self): # Publisher.subscribe(self.UpdateMEPPoints, "Update marker list") # Publisher.subscribe(self.SetBrainSurface, "Set MEP brain surface") Publisher.subscribe(self.UpdateMEPPoints, "Redraw MEP mapping") - Publisher.subscribe(self.UpdateMEPPointsFromBrainTargets, "Redraw MEP mapping from brain targets") + Publisher.subscribe( + self.UpdateMEPPointsFromBrainTargets, "Redraw MEP mapping from brain targets" + ) Publisher.subscribe(self.UpdateNavigationStatus, "Navigation status") Publisher.subscribe(self.SetBrainSurface, "Load brain surface actor") Publisher.subscribe(self.OnCloseProject, "Close project data") @@ -386,7 +388,9 @@ def UpdateMEPPointsFromBrainTargets(self): point_data.AddArray(mep_array) for marker in brain_markers: - points.InsertNextPoint(marker["position"][0], -marker["position"][1], marker["position"][2]) + points.InsertNextPoint( + marker["position"][0], -marker["position"][1], marker["position"][2] + ) mep_value = marker["mep_value"] or 0 mep_array.InsertNextValue(mep_value) MarkersControl().SaveState() diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 832e2ca99..eb39c64ec 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -2435,7 +2435,7 @@ def SetBrainTarget(self, brain_targets): marker.y_mtms = target["mtms"][1] marker.r_mtms = target["mtms"][2] marker.intensity_mtms = target["mtms"][3] - #TODO: MEP + # TODO: MEP marker.mep_value = 0 marker_target.brain_target_list.append(marker.to_brain_targets_dict()) From 57b6728647fa63ce94a4490c6c9f29d8ae910db1 Mon Sep 17 00:00:00 2001 From: rmatsuda Date: Fri, 13 Dec 2024 19:16:04 +0200 Subject: [PATCH 30/33] FIX: UI for brain_targets_list_ctrl --- invesalius/gui/task_navigator.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 6066002a2..40cf6bd6f 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -1203,6 +1203,12 @@ def __init__(self, parent, nav_hub): def __bind_events(self): Publisher.subscribe(self.OnCloseProject, "Close project data") + Publisher.subscribe(self.OnUpdateNavigationPanel, "Update navigation panel") + + def OnUpdateNavigationPanel(self): + self.sizer.Fit(self) + if self.GetParent().IsExpanded(): + self.GetParent().Fit() def OnCloseProject(self): self.tracker.ResetTrackerFiducials() @@ -2091,6 +2097,7 @@ def __init__(self, parent, nav_hub): # at most 1080 pixels (a commonly used height in laptops). Otherwise, the height grows linearly with # the screen height. marker_list_height = max(120, int(screen_height / 4)) + self.marker_list_height = marker_list_height marker_list_ctrl = wx.ListCtrl( self, -1, style=wx.LC_REPORT, size=wx.Size(0, marker_list_height) @@ -2143,7 +2150,9 @@ def __init__(self, parent, nav_hub): ) # Sub List Control - brain_targets_list_ctrl = wx.ListCtrl(self, style=wx.LC_REPORT) + brain_targets_list_ctrl = wx.ListCtrl( + self, style=wx.LC_REPORT, size=wx.Size(0, marker_list_height) + ) brain_targets_list_ctrl.InsertColumn(const.BRAIN_ID_COLUMN, "#") brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_ID_COLUMN, 26) @@ -2173,7 +2182,7 @@ def __init__(self, parent, nav_hub): brain_targets_list_ctrl.InsertColumn(const.BRAIN_UUID, "UUID") brain_targets_list_ctrl.SetColumnWidth(const.BRAIN_UUID, 45) - # self.brain_targets_list_ctrl.Hide() + brain_targets_list_ctrl.Hide() brain_targets_list_ctrl.Bind( wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnMouseRightDownBrainTargets @@ -2707,6 +2716,12 @@ def populate_sub_list(self, sub_items_list): ) Publisher.sendMessage("Update brain targets", brain_targets=brain_targets) + def ResizeListCtrl(self, width): + self.brain_targets_list_ctrl.SetMinSize((self.marker_list_ctrl.GetSize()[0], width)) + self.marker_list_ctrl.SetMinSize((self.marker_list_ctrl.GetSize()[0], width)) + self.brain_targets_list_ctrl.SetSize((self.marker_list_ctrl.GetSize()[0], width)) + self.marker_list_ctrl.SetSize((self.marker_list_ctrl.GetSize()[0], width)) + # Called when a marker on the list gets the focus by the user left-clicking on it. def OnMarkerFocused(self, evt): idx = self.marker_list_ctrl.GetFocusedItem() @@ -2743,10 +2758,13 @@ def OnMarkerFocused(self, evt): Publisher.sendMessage("Set vector field assembly visibility", enabled=True) self.populate_sub_list(marker.brain_target_list) self.brain_targets_list_ctrl.Show() + width = self.marker_list_height / 2 else: Publisher.sendMessage("Set vector field assembly visibility", enabled=False) self.brain_targets_list_ctrl.Hide() - self.Layout() # Refresh layout + width = self.marker_list_height + self.ResizeListCtrl(width) + Publisher.sendMessage("Update navigation panel") self.Update() # Called when a marker on the list loses the focus by the user left-clicking on another marker. From d4de4d6b9ae20fb8e3b032a9e8f4ab67b5177da7 Mon Sep 17 00:00:00 2001 From: Renan Date: Mon, 16 Dec 2024 07:49:18 +0200 Subject: [PATCH 31/33] FIX: minor brain mapping --- invesalius/constants.py | 8 ++++---- invesalius/gui/dialogs.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/invesalius/constants.py b/invesalius/constants.py index 00ffdd5b1..8200f23f8 100644 --- a/invesalius/constants.py +++ b/invesalius/constants.py @@ -1063,10 +1063,10 @@ "max": (1.0, 0.0, 0.0), }, "Viridis": { # Viridis (perceptually uniform) - "min": (0.267004, 0.004874, 0.329415), - "low": (0.226337, 0.31071, 0.577055), - "mid": (0.993248, 0.906157, 0.143936), - "max": (0.968627, 0.813008, 0.0), + "min": (0.267, 0.004, 0.329), + "low": (0.192, 0.408, 0.556), + "mid": (0.137, 0.718, 0.475), + "max": (0.993, 0.906, 0.144), }, "Grayscale": { # Grayscale (often used for CT/MRI) "min": (0.0, 0.0, 0.0), # Black diff --git a/invesalius/gui/dialogs.py b/invesalius/gui/dialogs.py index 0a73588a5..7333d700c 100644 --- a/invesalius/gui/dialogs.py +++ b/invesalius/gui/dialogs.py @@ -1207,9 +1207,9 @@ def ShowEnterMarkerID(default: str) -> str: def ShowEnterMEPValue(default): msg = _("Enter the MEP value (uV)") if sys.platform == "darwin": - dlg = wx.TextEntryDialog(None, "", msg, defaultValue=default) + dlg = wx.TextEntryDialog(None, "", msg, defaultValue=str(default)) else: - dlg = wx.TextEntryDialog(None, msg, "InVesalius 3", value=default) + dlg = wx.TextEntryDialog(None, msg, "InVesalius 3", value=str(default)) dlg.ShowModal() result = dlg.GetValue() # check if the value is a number From d9036c46182ce14a6a613b67a16f7e8a7664ffd9 Mon Sep 17 00:00:00 2001 From: Renan Date: Mon, 16 Dec 2024 07:51:37 +0200 Subject: [PATCH 32/33] RUFF --- invesalius/data/visualization/marker_visualizer.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/invesalius/data/visualization/marker_visualizer.py b/invesalius/data/visualization/marker_visualizer.py index 9482c2a57..2da97fa87 100644 --- a/invesalius/data/visualization/marker_visualizer.py +++ b/invesalius/data/visualization/marker_visualizer.py @@ -78,7 +78,9 @@ def __bind_events(self): Publisher.subscribe(self.UpdateBrainTargets, "Update brain targets") Publisher.subscribe(self.UpdateNavigationStatus, "Navigation status") Publisher.subscribe(self.UpdateTargetMode, "Set target mode") - Publisher.subscribe(self.UpdateVectorFieldAssemblyVisibility, "Set vector field assembly visibility") + Publisher.subscribe( + self.UpdateVectorFieldAssemblyVisibility, "Set vector field assembly visibility" + ) def UpdateNavigationStatus(self, nav_status, vis_status): self.is_navigating = nav_status @@ -97,7 +99,9 @@ def UpdateBrainTargets(self, brain_targets): Update the vector field assembly to reflect the current vector field. """ # Create a new vector field assembly. - new_vector_field_assembly = self.vector_field_visualizer.CreateVectorFieldAssembly(brain_targets) + new_vector_field_assembly = self.vector_field_visualizer.CreateVectorFieldAssembly( + brain_targets + ) # Replace the old vector field assembly with the new one. self.actor_factory.ReplaceActor( self.renderer, self.vector_field_assembly, new_vector_field_assembly From edc6becab91ccbf25a685dffc4da6c28d128b2ce Mon Sep 17 00:00:00 2001 From: Renan Date: Mon, 16 Dec 2024 07:53:06 +0200 Subject: [PATCH 33/33] RUFF --- invesalius/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/invesalius/constants.py b/invesalius/constants.py index 8200f23f8..fcb3f75fc 100644 --- a/invesalius/constants.py +++ b/invesalius/constants.py @@ -1066,7 +1066,7 @@ "min": (0.267, 0.004, 0.329), "low": (0.192, 0.408, 0.556), "mid": (0.137, 0.718, 0.475), - "max": (0.993, 0.906, 0.144), + "max": (0.993, 0.906, 0.144), }, "Grayscale": { # Grayscale (often used for CT/MRI) "min": (0.0, 0.0, 0.0), # Black