Commit 3b8f8e1ff7464fd3e9c576eda3d24dae75511c4e
Committed by
GitHub
Exists in
master
Merge pull request #403 from sotodela/278_add_visualization_for_the_direction_of_the_marker
278 add visualization for the direction of the marker
Showing
4 changed files
with
95 additions
and
60 deletions
Show diff stats
invesalius/constants.py
invesalius/data/styles.py
... | ... | @@ -486,7 +486,7 @@ class CrossInteractorStyle(DefaultInteractorStyle): |
486 | 486 | x, y, z = self.viewer.get_coordinate_cursor(mouse_x, mouse_y, self.picker) |
487 | 487 | self.viewer.UpdateSlicesPosition([x, y, z]) |
488 | 488 | # This "Set cross" message is needed to update the cross in the other slices |
489 | - Publisher.sendMessage('Set cross focal point', position=[x, y, z, 0., 0., 0.]) | |
489 | + Publisher.sendMessage('Set cross focal point', position=[x, y, z, None, None, None]) | |
490 | 490 | Publisher.sendMessage('Update slice viewer') |
491 | 491 | |
492 | 492 | def OnScrollBar(self, *args, **kwargs): |
... | ... | @@ -494,7 +494,7 @@ class CrossInteractorStyle(DefaultInteractorStyle): |
494 | 494 | # the actual orientation. |
495 | 495 | x, y, z = self.viewer.cross.GetFocalPoint() |
496 | 496 | self.viewer.UpdateSlicesPosition([x, y, z]) |
497 | - Publisher.sendMessage('Set cross focal point', position=[x, y, z, 0., 0., 0.]) | |
497 | + Publisher.sendMessage('Set cross focal point', position=[x, y, z, None, None, None]) | |
498 | 498 | Publisher.sendMessage('Update slice viewer') |
499 | 499 | |
500 | 500 | ... | ... |
invesalius/data/viewer_volume.py
... | ... | @@ -70,8 +70,8 @@ class Viewer(wx.Panel): |
70 | 70 | |
71 | 71 | self.initial_focus = None |
72 | 72 | |
73 | - self.staticballs = [] | |
74 | - | |
73 | + self.static_markers = [] | |
74 | + self.static_arrows = [] | |
75 | 75 | self.style = None |
76 | 76 | |
77 | 77 | interactor = wxVTKRenderWindowInteractor(self, -1, size = self.GetSize()) |
... | ... | @@ -278,6 +278,7 @@ class Viewer(wx.Panel): |
278 | 278 | |
279 | 279 | # Related to marker creation in navigation tools |
280 | 280 | Publisher.subscribe(self.AddMarker, 'Add marker') |
281 | + Publisher.subscribe(self.AddMarkerwithOrientation, 'Add arrow marker') | |
281 | 282 | Publisher.subscribe(self.HideAllMarkers, 'Hide all markers') |
282 | 283 | Publisher.subscribe(self.ShowAllMarkers, 'Show all markers') |
283 | 284 | Publisher.subscribe(self.RemoveAllMarkers, 'Remove all markers') |
... | ... | @@ -579,7 +580,7 @@ class Viewer(wx.Panel): |
579 | 580 | """ |
580 | 581 | Set all markers, overwriting the previous markers. |
581 | 582 | """ |
582 | - self.RemoveAllMarkers(len(self.staticballs)) | |
583 | + self.RemoveAllMarkers(len(self.static_markers)) | |
583 | 584 | |
584 | 585 | target_selected = False |
585 | 586 | for marker in markers: |
... | ... | @@ -607,6 +608,21 @@ class Viewer(wx.Panel): |
607 | 608 | |
608 | 609 | self.UpdateRender() |
609 | 610 | |
611 | + def AddMarkerwithOrientation(self, arrow_id, size, color, coord): | |
612 | + """ | |
613 | + Markers arrow with orientation created by navigation tools and rendered in volume viewer. | |
614 | + """ | |
615 | + self.arrow_marker_id = arrow_id | |
616 | + coord_flip = list(coord) | |
617 | + coord_flip[1] = -coord_flip[1] | |
618 | + | |
619 | + arrow_actor = self.Add_ObjectArrow(coord_flip[:3], coord_flip[3:6], color, size) | |
620 | + self.static_markers.append(arrow_actor) | |
621 | + self.ren.AddActor(self.static_markers[self.arrow_marker_id]) | |
622 | + self.arrow_marker_id += 1 | |
623 | + | |
624 | + self.Refresh() | |
625 | + | |
610 | 626 | def AddMarker(self, ball_id, size, colour, coord): |
611 | 627 | """ |
612 | 628 | Markers created by navigation tools and rendered in volume viewer. |
... | ... | @@ -626,12 +642,12 @@ class Viewer(wx.Panel): |
626 | 642 | prop.SetColor(colour) |
627 | 643 | |
628 | 644 | # adding a new actor for the present ball |
629 | - self.staticballs.append(vtk.vtkActor()) | |
645 | + self.static_markers.append(vtk.vtkActor()) | |
630 | 646 | |
631 | - self.staticballs[self.ball_id].SetMapper(mapper) | |
632 | - self.staticballs[self.ball_id].SetProperty(prop) | |
647 | + self.static_markers[self.ball_id].SetMapper(mapper) | |
648 | + self.static_markers[self.ball_id].SetProperty(prop) | |
633 | 649 | |
634 | - self.ren.AddActor(self.staticballs[self.ball_id]) | |
650 | + self.ren.AddActor(self.static_markers[self.ball_id]) | |
635 | 651 | self.ball_id += 1 |
636 | 652 | |
637 | 653 | #self.UpdateRender() |
... | ... | @@ -667,33 +683,33 @@ class Viewer(wx.Panel): |
667 | 683 | def HideAllMarkers(self, indexes): |
668 | 684 | ballid = indexes |
669 | 685 | for i in range(0, ballid): |
670 | - self.staticballs[i].SetVisibility(0) | |
686 | + self.static_markers[i].SetVisibility(0) | |
671 | 687 | self.UpdateRender() |
672 | 688 | |
673 | 689 | def ShowAllMarkers(self, indexes): |
674 | 690 | ballid = indexes |
675 | 691 | for i in range(0, ballid): |
676 | - self.staticballs[i].SetVisibility(1) | |
692 | + self.static_markers[i].SetVisibility(1) | |
677 | 693 | self.UpdateRender() |
678 | 694 | |
679 | 695 | def RemoveAllMarkers(self, indexes): |
680 | 696 | ballid = indexes |
681 | 697 | for i in range(0, ballid): |
682 | - self.ren.RemoveActor(self.staticballs[i]) | |
683 | - self.staticballs = [] | |
698 | + self.ren.RemoveActor(self.static_markers[i]) | |
699 | + self.static_markers = [] | |
684 | 700 | self.UpdateRender() |
685 | 701 | |
686 | 702 | def RemoveMultipleMarkers(self, index): |
687 | 703 | for i in reversed(index): |
688 | - self.ren.RemoveActor(self.staticballs[i]) | |
689 | - del self.staticballs[i] | |
704 | + self.ren.RemoveActor(self.static_markers[i]) | |
705 | + del self.static_markers[i] | |
690 | 706 | self.ball_id = self.ball_id - 1 |
691 | 707 | self.UpdateRender() |
692 | 708 | |
693 | 709 | def BlinkMarker(self, index): |
694 | 710 | if self.timer: |
695 | 711 | self.timer.Stop() |
696 | - self.staticballs[self.index].SetVisibility(1) | |
712 | + self.static_markers[self.index].SetVisibility(1) | |
697 | 713 | self.index = index |
698 | 714 | self.timer = wx.Timer(self) |
699 | 715 | self.Bind(wx.EVT_TIMER, self.OnBlinkMarker, self.timer) |
... | ... | @@ -701,7 +717,7 @@ class Viewer(wx.Panel): |
701 | 717 | self.timer_count = 0 |
702 | 718 | |
703 | 719 | def OnBlinkMarker(self, evt): |
704 | - self.staticballs[self.index].SetVisibility(int(self.timer_count % 2)) | |
720 | + self.static_markers[self.index].SetVisibility(int(self.timer_count % 2)) | |
705 | 721 | self.Refresh() |
706 | 722 | self.timer_count += 1 |
707 | 723 | |
... | ... | @@ -709,20 +725,20 @@ class Viewer(wx.Panel): |
709 | 725 | if self.timer: |
710 | 726 | self.timer.Stop() |
711 | 727 | if index is None: |
712 | - self.staticballs[self.index].SetVisibility(1) | |
728 | + self.static_markers[self.index].SetVisibility(1) | |
713 | 729 | self.Refresh() |
714 | 730 | self.index = False |
715 | 731 | |
716 | 732 | def SetNewColor(self, index, color): |
717 | - self.staticballs[index].GetProperty().SetColor([round(s/255.0, 3) for s in color]) | |
733 | + self.static_markers[index].GetProperty().SetColor([round(s / 255.0, 3) for s in color]) | |
718 | 734 | self.Refresh() |
719 | 735 | |
720 | 736 | def OnTargetMarkerTransparency(self, status, index): |
721 | 737 | if status: |
722 | - self.staticballs[index].GetProperty().SetOpacity(1) | |
738 | + self.static_markers[index].GetProperty().SetOpacity(1) | |
723 | 739 | # self.staticballs[index].GetProperty().SetOpacity(0.4) |
724 | 740 | else: |
725 | - self.staticballs[index].GetProperty().SetOpacity(1) | |
741 | + self.static_markers[index].GetProperty().SetOpacity(1) | |
726 | 742 | |
727 | 743 | def OnUpdateAngleThreshold(self, angle): |
728 | 744 | self.anglethreshold = angle |
... | ... | @@ -992,16 +1008,10 @@ class Viewer(wx.Panel): |
992 | 1008 | self.RemoveTarget() |
993 | 1009 | self.DisableCoilTracker() |
994 | 1010 | |
995 | - def CreateTargetAim(self): | |
996 | - if self.aim_actor: | |
997 | - self.RemoveTargetAim() | |
998 | - self.aim_actor = None | |
999 | - | |
1000 | - vtk_colors = vtk.vtkNamedColors() | |
1001 | - | |
1011 | + def CreateVTKObjectMatrix(self, direction, orientation): | |
1002 | 1012 | m_img = dco.coordinates_to_transformation_matrix( |
1003 | - position=self.target_coord[:3], | |
1004 | - orientation=self.target_coord[3:], | |
1013 | + position=direction, | |
1014 | + orientation=orientation, | |
1005 | 1015 | axes='sxyz', |
1006 | 1016 | ) |
1007 | 1017 | m_img = np.asmatrix(m_img) |
... | ... | @@ -1012,7 +1022,16 @@ class Viewer(wx.Panel): |
1012 | 1022 | for col in range(0, 4): |
1013 | 1023 | m_img_vtk.SetElement(row, col, m_img[row, col]) |
1014 | 1024 | |
1015 | - self.m_img_vtk = m_img_vtk | |
1025 | + return m_img_vtk | |
1026 | + | |
1027 | + def CreateTargetAim(self): | |
1028 | + if self.aim_actor: | |
1029 | + self.RemoveTargetAim() | |
1030 | + self.aim_actor = None | |
1031 | + | |
1032 | + vtk_colors = vtk.vtkNamedColors() | |
1033 | + | |
1034 | + self.m_img_vtk = self.CreateVTKObjectMatrix(self.target_coord[:3], self.target_coord[3:]) | |
1016 | 1035 | |
1017 | 1036 | filename = os.path.join(inv_paths.OBJ_DIR, "aim.stl") |
1018 | 1037 | |
... | ... | @@ -1023,7 +1042,7 @@ class Viewer(wx.Panel): |
1023 | 1042 | |
1024 | 1043 | # Transform the polydata |
1025 | 1044 | transform = vtk.vtkTransform() |
1026 | - transform.SetMatrix(m_img_vtk) | |
1045 | + transform.SetMatrix(self.m_img_vtk) | |
1027 | 1046 | transformPD = vtk.vtkTransformPolyDataFilter() |
1028 | 1047 | transformPD.SetTransform(transform) |
1029 | 1048 | transformPD.SetInputConnection(reader.GetOutputPort()) |
... | ... | @@ -1071,7 +1090,7 @@ class Viewer(wx.Panel): |
1071 | 1090 | self.dummy_coil_actor.GetProperty().SetSpecularPower(10) |
1072 | 1091 | self.dummy_coil_actor.GetProperty().SetOpacity(.3) |
1073 | 1092 | self.dummy_coil_actor.SetVisibility(1) |
1074 | - self.dummy_coil_actor.SetUserMatrix(m_img_vtk) | |
1093 | + self.dummy_coil_actor.SetUserMatrix(self.m_img_vtk) | |
1075 | 1094 | |
1076 | 1095 | self.ren.AddActor(self.dummy_coil_actor) |
1077 | 1096 | |
... | ... | @@ -1424,8 +1443,9 @@ class Viewer(wx.Panel): |
1424 | 1443 | actor.GetProperty().SetLineWidth(5) |
1425 | 1444 | actor.AddPosition(0, 0, 0) |
1426 | 1445 | actor.SetScale(size) |
1427 | - actor.SetPosition(direction) | |
1428 | - actor.SetOrientation(orientation) | |
1446 | + | |
1447 | + m_img_vtk = self.CreateVTKObjectMatrix(direction, orientation) | |
1448 | + actor.SetUserMatrix(m_img_vtk) | |
1429 | 1449 | |
1430 | 1450 | return actor |
1431 | 1451 | ... | ... |
invesalius/gui/task_navigator.py
... | ... | @@ -359,7 +359,7 @@ class NeuronavigationPanel(wx.Panel): |
359 | 359 | |
360 | 360 | self.nav_status = False |
361 | 361 | self.tracker_fiducial_being_set = None |
362 | - self.current_coord = 0, 0, 0 | |
362 | + self.current_coord = 0, 0, 0, None, None, None | |
363 | 363 | |
364 | 364 | # Initialize list of buttons and numctrls for wx objects |
365 | 365 | self.btns_set_fiducial = [None, None, None, None, None, None] |
... | ... | @@ -704,7 +704,7 @@ class NeuronavigationPanel(wx.Panel): |
704 | 704 | if self.btns_set_fiducial[n].GetValue(): |
705 | 705 | coord = self.numctrls_fiducial[n][0].GetValue(),\ |
706 | 706 | self.numctrls_fiducial[n][1].GetValue(),\ |
707 | - self.numctrls_fiducial[n][2].GetValue(), 0, 0, 0 | |
707 | + self.numctrls_fiducial[n][2].GetValue(), None, None, None | |
708 | 708 | |
709 | 709 | Publisher.sendMessage('Set image fiducial', fiducial_name=fiducial_name, coord=coord[0:3]) |
710 | 710 | |
... | ... | @@ -1144,9 +1144,9 @@ class MarkersPanel(wx.Panel): |
1144 | 1144 | x : float = 0 |
1145 | 1145 | y : float = 0 |
1146 | 1146 | z : float = 0 |
1147 | - alpha : float = 0 | |
1148 | - beta : float = 0 | |
1149 | - gamma : float = 0 | |
1147 | + alpha : float = dataclasses.field(default = None) | |
1148 | + beta : float = dataclasses.field(default = None) | |
1149 | + gamma : float = dataclasses.field(default = None) | |
1150 | 1150 | r : float = 0 |
1151 | 1151 | g : float = 1 |
1152 | 1152 | b : float = 0 |
... | ... | @@ -1161,7 +1161,7 @@ class MarkersPanel(wx.Panel): |
1161 | 1161 | # x, y, z, alpha, beta, gamma can be jointly accessed as coord |
1162 | 1162 | @property |
1163 | 1163 | def coord(self): |
1164 | - return list((self.x, self.y, self.z, self.alpha, self.beta, self.gamma),) | |
1164 | + return list((self.x, self.y, self.z, self.alpha, self.beta, self.gamma)) | |
1165 | 1165 | |
1166 | 1166 | @coord.setter |
1167 | 1167 | def coord(self, new_coord): |
... | ... | @@ -1201,11 +1201,18 @@ class MarkersPanel(wx.Panel): |
1201 | 1201 | else: |
1202 | 1202 | res += ('%s\t' % str(getattr(self, field.name))) |
1203 | 1203 | |
1204 | - # Add world coordinates (in addition to the internal ones). | |
1205 | - position_world, orientation_world = imagedata_utils.convert_invesalius_to_world( | |
1206 | - position=[self.x, self.y, self.z], | |
1207 | - orientation=[self.alpha, self.beta, self.gamma], | |
1208 | - ) | |
1204 | + if self.alpha is not None and self.beta is not None and self.gamma is not None: | |
1205 | + # Add world coordinates (in addition to the internal ones). | |
1206 | + position_world, orientation_world = imagedata_utils.convert_invesalius_to_world( | |
1207 | + position=[self.x, self.y, self.z], | |
1208 | + orientation=[self.alpha, self.beta, self.gamma], | |
1209 | + ) | |
1210 | + | |
1211 | + else: | |
1212 | + position_world, orientation_world = imagedata_utils.convert_invesalius_to_world( | |
1213 | + position=[self.x, self.y, self.z], | |
1214 | + orientation=[0,0,0], | |
1215 | + ) | |
1209 | 1216 | |
1210 | 1217 | res += '\t'.join(map(lambda x: 'N/A' if x is None else str(x), (*position_world, *orientation_world))) |
1211 | 1218 | return res |
... | ... | @@ -1215,8 +1222,10 @@ class MarkersPanel(wx.Panel): |
1215 | 1222 | properly formatted, might throw an exception and leave the object |
1216 | 1223 | in an inconsistent state.""" |
1217 | 1224 | for field, str_val in zip(dataclasses.fields(self.__class__), inp_str.split('\t')): |
1218 | - if field.type is float: | |
1225 | + if field.type is float and str_val != 'None': | |
1219 | 1226 | setattr(self, field.name, float(str_val)) |
1227 | + if field.type is float and str_val == 'None': | |
1228 | + setattr(self, field.name, None) | |
1220 | 1229 | if field.type is int: |
1221 | 1230 | setattr(self, field.name, int(str_val)) |
1222 | 1231 | if field.type is str: |
... | ... | @@ -1253,8 +1262,7 @@ class MarkersPanel(wx.Panel): |
1253 | 1262 | |
1254 | 1263 | self.session = ses.Session() |
1255 | 1264 | |
1256 | - self.current_coord = 0, 0, 0, 0, 0, 0 | |
1257 | - self.current_angle = 0, 0, 0 | |
1265 | + self.current_coord = 0, 0, 0, None, None, None | |
1258 | 1266 | self.current_seed = 0, 0, 0 |
1259 | 1267 | self.current_robot_target_matrix = [None] * 9 |
1260 | 1268 | self.markers = [] |
... | ... | @@ -1264,6 +1272,7 @@ class MarkersPanel(wx.Panel): |
1264 | 1272 | |
1265 | 1273 | self.marker_colour = const.MARKER_COLOUR |
1266 | 1274 | self.marker_size = const.MARKER_SIZE |
1275 | + self.arrow_marker_size = const.ARROW_MARKER_SIZE | |
1267 | 1276 | self.current_session = 1 |
1268 | 1277 | |
1269 | 1278 | # Change marker size |
... | ... | @@ -1431,13 +1440,11 @@ class MarkersPanel(wx.Panel): |
1431 | 1440 | return list(itertools.chain(*(const.BTNS_IMG_MARKERS[i].values() for i in const.BTNS_IMG_MARKERS))) |
1432 | 1441 | |
1433 | 1442 | def UpdateCurrentCoord(self, position): |
1434 | - self.current_coord = position | |
1435 | - #self.current_angle = pubsub_evt.data[1][3:] | |
1443 | + self.current_coord = list(position) | |
1436 | 1444 | |
1437 | 1445 | def UpdateNavigationStatus(self, nav_status, vis_status): |
1438 | 1446 | if not nav_status: |
1439 | - sleep(0.5) | |
1440 | - #self.current_coord[3:] = 0, 0, 0 | |
1447 | + self.current_coord[3:] = None, None, None | |
1441 | 1448 | self.nav_status = False |
1442 | 1449 | else: |
1443 | 1450 | self.nav_status = True |
... | ... | @@ -1615,8 +1622,8 @@ class MarkersPanel(wx.Panel): |
1615 | 1622 | if marker.is_target: |
1616 | 1623 | self.__set_marker_as_target(len(self.markers) - 1) |
1617 | 1624 | |
1618 | - except: | |
1619 | - wx.MessageBox(_("Invalid markers file."), _("InVesalius 3")) | |
1625 | + except Exception as e: | |
1626 | + wx.MessageBox(_("Invalid markers file."), _("InVesalius 3")) | |
1620 | 1627 | |
1621 | 1628 | def OnMarkersVisibility(self, evt, ctrl): |
1622 | 1629 | if ctrl.GetValue(): |
... | ... | @@ -1681,10 +1688,18 @@ class MarkersPanel(wx.Panel): |
1681 | 1688 | new_robot_marker.robot_target_matrix = self.current_robot_target_matrix |
1682 | 1689 | |
1683 | 1690 | # Note that ball_id is zero-based, so we assign it len(self.markers) before the new marker is added |
1684 | - Publisher.sendMessage('Add marker', ball_id=len(self.markers), | |
1685 | - size=new_marker.size, | |
1686 | - colour=new_marker.colour, | |
1687 | - coord=new_marker.coord[:3]) | |
1691 | + if all([elem is not None for elem in new_marker.coord[3:]]): | |
1692 | + Publisher.sendMessage('Add arrow marker', arrow_id=len(self.markers), | |
1693 | + size=self.arrow_marker_size, | |
1694 | + color=new_marker.colour, | |
1695 | + coord=new_marker.coord) | |
1696 | + else: | |
1697 | + Publisher.sendMessage('Add marker', ball_id=len(self.markers), | |
1698 | + size=new_marker.size, | |
1699 | + colour=new_marker.colour, | |
1700 | + coord=new_marker.coord[:3]) | |
1701 | + | |
1702 | + | |
1688 | 1703 | self.markers.append(new_marker) |
1689 | 1704 | self.robot_markers.append(new_robot_marker) |
1690 | 1705 | ... | ... |