Commit 9de6858764937e33dd2090be46587d1b9659d8b2
Committed by
GitHub
1 parent
85de3a6a
Exists in
master
Coil projection on peel (#308)
* add add_object_orientation_disk and add_addobjectArrow functions, add orientation disk and projection arrow actors * add getCenter and getNormal functions, create locator and add self.peel_centers and self.peel_normals variables, add to calculate normals and centers when calling getPeelActor (new peel) * add GetPeelCenters and Initlocator_viewer and corresponding publisher call * add in OnLinkBrain and TractographyPanel class (OnSelectPeelingDepth) call to get peel centers and normals and initiator) * remove print on GetPeelCenters and Initlocator_viewer * add objectArrowlocation to calculate coil norms and directions * Add UpdateObjectArrowOrientation and Getcellintersection - getcellintersection function calculates the intersection between the coil normal and the peel center -calculates the angle between the coil normal and the normal of the center of the intersected cell -shows the coil projected with arrow and disk -Color of the disk and arrow change according to the value of the angle between the normal of the coil and the peel center mesh intersected. -updateobjectarroworientation is used to show and update the position of the projected arrow and disk * add comments, removed not used code on class def, modify currentPeel as local variable * modify getPeelActor removing input currentPeel * add affine_vtk matrix as input to getPeelActor, add change position of curent peel * add case when not intersection found in getcellintersection function * add remove arrow and disk actor only when intersecting with the peel * add comments to getcellintersection function, remove TODO * add comment to getcellintersection * add transformpeelposition function * add setvisibility to arrow and disk actor on OnNavigationStatus * add tangent lines for debuggint at getcellintersection function * add coil_dir from coordinates from tracker to objectArrowlocation * modify visibility of disk and coil projection arrow, to be shown when coil is not shown * comment tangent lines for debuggin on getcellintersection * modify names of functions to invesalius naming convention * correct mispelling error when calling GetCellIntersection * add angle threshold for projectio arrow in constansts * add angle threshold for arrow projection in constants * change size of disk and arrow in coil projection * add normal lines for debugging arrow projection * Modify function names to invensalius convention * add condition for track if peel is loaded or not * fix removing arrow and disk projection when distance is larger than set value, removing normal lines for debugging * clean comments * add self.peel_loaded to vis_components * Modify UpdatePeelVisualization to pass peel_loaded from navigation class * change color disk and arrow
Showing
4 changed files
with
269 additions
and
47 deletions
Show diff stats
invesalius/constants.py
invesalius/data/brainmesh_handler.py
... | ... | @@ -8,34 +8,33 @@ import pyvista |
8 | 8 | |
9 | 9 | class Brain: |
10 | 10 | def __init__(self, img_path, mask_path, n_peels, affine_vtk): |
11 | + # Create arrays to access the peel data and peel Actors | |
11 | 12 | self.peel = [] |
12 | 13 | self.peelActors = [] |
13 | - | |
14 | + # Read the image | |
14 | 15 | T1_reader = vtk.vtkNIFTIImageReader() |
15 | 16 | T1_reader.SetFileName(img_path) |
16 | 17 | T1_reader.Update() |
17 | - | |
18 | - # self.refImage = vtk.vtkImageData() | |
18 | + # Image | |
19 | 19 | self.refImage = T1_reader.GetOutput() |
20 | - | |
20 | + # Read the mask | |
21 | 21 | mask_reader = vtk.vtkNIFTIImageReader() |
22 | 22 | mask_reader.SetFileName(mask_path) |
23 | 23 | mask_reader.Update() |
24 | - | |
24 | + # Use the mask to create isosurface | |
25 | 25 | mc = vtk.vtkContourFilter() |
26 | 26 | mc.SetInputConnection(mask_reader.GetOutputPort()) |
27 | 27 | mc.SetValue(0, 1) |
28 | 28 | mc.Update() |
29 | 29 | |
30 | - refSurface = vtk.vtkPolyData() | |
30 | + # Mask isosurface | |
31 | 31 | refSurface = mc.GetOutput() |
32 | - | |
33 | - tmpPeel = vtk.vtkPolyData() | |
32 | + # Create a uniformly meshed surface | |
34 | 33 | tmpPeel = downsample(refSurface) |
35 | - | |
36 | - mask_sFormMatrix = vtk.vtkMatrix4x4() | |
34 | + # Standard space coordinates | |
37 | 35 | mask_sFormMatrix = mask_reader.GetSFormMatrix() |
38 | 36 | |
37 | + # Apply coordinate transform to the meshed mask | |
39 | 38 | mask_ijk2xyz = vtk.vtkTransform() |
40 | 39 | mask_ijk2xyz.SetMatrix(mask_sFormMatrix) |
41 | 40 | |
... | ... | @@ -44,17 +43,21 @@ class Brain: |
44 | 43 | mask_ijk2xyz_filter.SetTransform(mask_ijk2xyz) |
45 | 44 | mask_ijk2xyz_filter.Update() |
46 | 45 | |
46 | + # Smooth the mesh | |
47 | 47 | tmpPeel = smooth(mask_ijk2xyz_filter.GetOutput()) |
48 | + # Configure calculation of normals | |
48 | 49 | tmpPeel = fixMesh(tmpPeel) |
50 | + # Remove duplicate points etc | |
49 | 51 | tmpPeel = cleanMesh(tmpPeel) |
52 | + # Generate triangles | |
50 | 53 | tmpPeel = upsample(tmpPeel) |
54 | + | |
51 | 55 | tmpPeel = smooth(tmpPeel) |
52 | 56 | tmpPeel = fixMesh(tmpPeel) |
53 | 57 | tmpPeel = cleanMesh(tmpPeel) |
54 | 58 | |
55 | - # sFormMatrix = vtk.vtkMatrix4x4() | |
59 | + # Scanner coordinates from image | |
56 | 60 | qFormMatrix = T1_reader.GetQFormMatrix() |
57 | - # sFormMatrix = T1_reader.GetSFormMatrix() | |
58 | 61 | |
59 | 62 | refImageSpace2_xyz_transform = vtk.vtkTransform() |
60 | 63 | refImageSpace2_xyz_transform.SetMatrix(qFormMatrix) |
... | ... | @@ -69,29 +72,32 @@ class Brain: |
69 | 72 | self.xyz2_refImageSpace = vtk.vtkTransformPolyDataFilter() |
70 | 73 | self.xyz2_refImageSpace.SetTransform(xyz2_refImageSpace_transform) |
71 | 74 | |
72 | - # self.currentPeel = vtk.vtkPolyData() | |
73 | - self.currentPeel = tmpPeel | |
75 | + currentPeel = tmpPeel | |
74 | 76 | self.currentPeelNo = 0 |
75 | - self.mapImageOnCurrentPeel() | |
77 | + currentPeel= self.MapImageOnCurrentPeel(currentPeel) | |
76 | 78 | |
77 | 79 | newPeel = vtk.vtkPolyData() |
78 | - newPeel.DeepCopy(self.currentPeel) | |
80 | + newPeel.DeepCopy(currentPeel) | |
81 | + newPeel.DeepCopy(currentPeel) | |
82 | + self.peel_normals = vtk.vtkFloatArray() | |
83 | + self.peel_centers = vtk.vtkFloatArray() | |
79 | 84 | self.peel.append(newPeel) |
80 | 85 | self.currentPeelActor = vtk.vtkActor() |
81 | 86 | self.currentPeelActor.SetUserMatrix(affine_vtk) |
82 | - self.getCurrentPeelActor() | |
87 | + self.GetCurrentPeelActor(currentPeel) | |
83 | 88 | self.peelActors.append(self.currentPeelActor) |
84 | - | |
89 | + # locator will later find the triangle on the peel surface where the coil's normal intersect | |
90 | + self.locator = vtk.vtkCellLocator() | |
85 | 91 | self.numberOfPeels = n_peels |
86 | - self.peelDown() | |
92 | + self.PeelDown(currentPeel) | |
87 | 93 | |
88 | - def get_actor(self, n): | |
89 | - return self.getPeelActor(n) | |
94 | + def get_actor(self, n, affine_vtk): | |
95 | + return self.GetPeelActor(n, affine_vtk) | |
90 | 96 | |
91 | - def sliceDown(self): | |
97 | + def SliceDown(self, currentPeel): | |
92 | 98 | # Warp using the normals |
93 | 99 | warp = vtk.vtkWarpVector() |
94 | - warp.SetInputData(fixMesh(downsample(self.currentPeel))) # fixMesh here updates normals needed for warping | |
100 | + warp.SetInputData(fixMesh(downsample(currentPeel))) # fixMesh here updates normals needed for warping | |
95 | 101 | warp.SetInputArrayToProcess(0, 0, 0, vtk.vtkDataObject().FIELD_ASSOCIATION_POINTS, |
96 | 102 | vtk.vtkDataSetAttributes().NORMALS) |
97 | 103 | warp.SetScaleFactor(-1) |
... | ... | @@ -103,8 +109,8 @@ class Brain: |
103 | 109 | out = fixMesh(out) |
104 | 110 | out = cleanMesh(out) |
105 | 111 | |
106 | - self.currentPeel = out | |
107 | - | |
112 | + currentPeel = out | |
113 | + return currentPeel | |
108 | 114 | # def sliceUp(self): |
109 | 115 | # # Warp using the normals |
110 | 116 | # warp = vtk.vtkWarpVector() |
... | ... | @@ -122,8 +128,8 @@ class Brain: |
122 | 128 | # |
123 | 129 | # currentPeel = out |
124 | 130 | |
125 | - def mapImageOnCurrentPeel(self): | |
126 | - self.xyz2_refImageSpace.SetInputData(self.currentPeel) | |
131 | + def MapImageOnCurrentPeel(self, currentPeel): | |
132 | + self.xyz2_refImageSpace.SetInputData(currentPeel) | |
127 | 133 | self.xyz2_refImageSpace.Update() |
128 | 134 | |
129 | 135 | probe = vtk.vtkProbeFilter() |
... | ... | @@ -134,25 +140,36 @@ class Brain: |
134 | 140 | self.refImageSpace2_xyz.SetInputData(probe.GetOutput()) |
135 | 141 | self.refImageSpace2_xyz.Update() |
136 | 142 | |
137 | - self.currentPeel = self.refImageSpace2_xyz.GetOutput() | |
143 | + currentPeel = self.refImageSpace2_xyz.GetOutput() | |
144 | + return currentPeel | |
138 | 145 | |
139 | - def peelDown(self): | |
146 | + def PeelDown(self, currentPeel): | |
140 | 147 | for i in range(0, self.numberOfPeels): |
141 | - self.sliceDown() | |
142 | - self.mapImageOnCurrentPeel() | |
148 | + currentPeel = self.SliceDown(currentPeel) | |
149 | + currentPeel = self.MapImageOnCurrentPeel(currentPeel) | |
143 | 150 | |
144 | 151 | newPeel = vtk.vtkPolyData() |
145 | - newPeel.DeepCopy(self.currentPeel) | |
152 | + newPeel.DeepCopy(currentPeel) | |
146 | 153 | self.peel.append(newPeel) |
147 | 154 | |
148 | - # getCurrentPeelActor() | |
155 | + # GetCurrentPeelActor() | |
149 | 156 | # newPeelActor = vtk.vtkActor() |
150 | 157 | # newPeelActor = currentPeelActor |
151 | 158 | # peelActors.push_back(newPeelActor) |
152 | 159 | |
153 | 160 | self.currentPeelNo += 1 |
154 | 161 | |
155 | - def getPeelActor(self, p): | |
162 | + def TransformPeelPosition(self, p, affine_vtk): | |
163 | + peel_transform = vtk.vtkTransform() | |
164 | + peel_transform.SetMatrix(affine_vtk) | |
165 | + refpeelspace = vtk.vtkTransformPolyDataFilter() | |
166 | + refpeelspace.SetInputData(self.peel[p]) | |
167 | + refpeelspace.SetTransform(peel_transform) | |
168 | + refpeelspace.Update() | |
169 | + currentPeel = refpeelspace.GetOutput() | |
170 | + return currentPeel | |
171 | + | |
172 | + def GetPeelActor(self, p, affine_vtk): | |
156 | 173 | colors = vtk.vtkNamedColors() |
157 | 174 | # Create the color map |
158 | 175 | colorLookupTable = vtk.vtkLookupTable() |
... | ... | @@ -179,9 +196,16 @@ class Brain: |
179 | 196 | # Set actor |
180 | 197 | self.currentPeelActor.SetMapper(mapper) |
181 | 198 | |
199 | + currentPeel = self.TransformPeelPosition(p, affine_vtk) | |
200 | + | |
201 | + self.locator.SetDataSet(currentPeel) | |
202 | + self.locator.BuildLocator() | |
203 | + self.GetCenters(currentPeel) | |
204 | + self.GetNormals(currentPeel) | |
205 | + | |
182 | 206 | return self.currentPeelActor |
183 | 207 | |
184 | - def getCurrentPeelActor(self): | |
208 | + def GetCurrentPeelActor(self, currentPeel): | |
185 | 209 | colors = vtk.vtkNamedColors() |
186 | 210 | |
187 | 211 | # Create the color map |
... | ... | @@ -198,7 +222,7 @@ class Brain: |
198 | 222 | |
199 | 223 | # Set mapper auto |
200 | 224 | mapper = vtk.vtkOpenGLPolyDataMapper() |
201 | - mapper.SetInputData(self.currentPeel) | |
225 | + mapper.SetInputData(currentPeel) | |
202 | 226 | # mapper.SetScalarRange(0, 1000) |
203 | 227 | # mapper.SetScalarRange(0, 250) |
204 | 228 | mapper.SetScalarRange(0, 200) |
... | ... | @@ -213,6 +237,26 @@ class Brain: |
213 | 237 | |
214 | 238 | return self.currentPeelActor |
215 | 239 | |
240 | + def GetCenters(self, currentPeel): | |
241 | + # Compute centers of triangles | |
242 | + centerComputer = vtk.vtkCellCenters() # This computes centers of the triangles on the peel | |
243 | + centerComputer.SetInputData(currentPeel) | |
244 | + centerComputer.Update() | |
245 | + # This stores the centers for easy access | |
246 | + peel_centers = centerComputer.GetOutput() | |
247 | + self.peel_centers = peel_centers | |
248 | + | |
249 | + def GetNormals(self, currentPeel): | |
250 | + # Compute normals of triangles | |
251 | + normalComputer = vtk.vtkPolyDataNormals() # This computes normals of the triangles on the peel | |
252 | + normalComputer.SetInputData(currentPeel) | |
253 | + normalComputer.ComputePointNormalsOff() | |
254 | + normalComputer.ComputeCellNormalsOn() | |
255 | + normalComputer.Update() | |
256 | + # This converts to the normals to an array for easy access | |
257 | + peel_normals = normalComputer.GetOutput().GetCellData().GetNormals() | |
258 | + self.peel_normals = peel_normals | |
259 | + | |
216 | 260 | |
217 | 261 | def cleanMesh(inp): |
218 | 262 | cleaned = vtk.vtkCleanPolyData() | ... | ... |
invesalius/data/viewer_volume.py
... | ... | @@ -173,6 +173,8 @@ class Viewer(wx.Panel): |
173 | 173 | self.y_actor = None |
174 | 174 | self.z_actor = None |
175 | 175 | self.mark_actor = None |
176 | + self.obj_projection_arrow_actor = None | |
177 | + self.object_orientation_disk_actor = None | |
176 | 178 | |
177 | 179 | self._mode_cross = False |
178 | 180 | self._to_show_ball = 0 |
... | ... | @@ -192,6 +194,7 @@ class Viewer(wx.Panel): |
192 | 194 | self.polydata = None |
193 | 195 | self.anglethreshold = const.COIL_ANGLES_THRESHOLD |
194 | 196 | self.distthreshold = const.COIL_COORD_THRESHOLD |
197 | + self.angle_arrow_projection_threshold = const.COIL_ANGLE_ARROW_PROJECTION_THRESHOLD | |
195 | 198 | |
196 | 199 | self.actor_tracts = None |
197 | 200 | self.actor_peel = None |
... | ... | @@ -293,6 +296,7 @@ class Viewer(wx.Panel): |
293 | 296 | # Related to object tracking during neuronavigation |
294 | 297 | Publisher.subscribe(self.OnNavigationStatus, 'Navigation status') |
295 | 298 | Publisher.subscribe(self.UpdateObjectOrientation, 'Update object matrix') |
299 | + Publisher.subscribe(self.UpdateObjectArrowOrientation, 'Update object arrow matrix') | |
296 | 300 | Publisher.subscribe(self.UpdateTrackObjectState, 'Update track object state') |
297 | 301 | Publisher.subscribe(self.UpdateShowObjectState, 'Update show object state') |
298 | 302 | |
... | ... | @@ -310,6 +314,8 @@ class Viewer(wx.Panel): |
310 | 314 | Publisher.subscribe(self.UpdateMarkerOffsetState, 'Update marker offset state') |
311 | 315 | Publisher.subscribe(self.UpdateMarkerOffsetPosition, 'Update marker offset') |
312 | 316 | Publisher.subscribe(self.AddPeeledSurface, 'Update peel') |
317 | + Publisher.subscribe(self.GetPeelCenters, 'Get peel centers and normals') | |
318 | + Publisher.subscribe(self.Initlocator_viewer, 'Get init locator') | |
313 | 319 | |
314 | 320 | Publisher.subscribe(self.load_mask_preview, 'Load mask preview') |
315 | 321 | Publisher.subscribe(self.remove_mask_preview, 'Remove mask preview') |
... | ... | @@ -1320,11 +1326,18 @@ class Viewer(wx.Panel): |
1320 | 1326 | self.y_actor = self.add_line([0., 0., 0.], [0., 1., 0.], color=[.0, 1.0, .0]) |
1321 | 1327 | self.z_actor = self.add_line([0., 0., 0.], [0., 0., 1.], color=[1.0, .0, .0]) |
1322 | 1328 | |
1329 | + self.obj_projection_arrow_actor = self.Add_ObjectArrow([0., 0., 0.], [0., 0., 0.], vtk_colors.GetColor3d('Red'), | |
1330 | + 15) | |
1331 | + self.object_orientation_disk_actor = self.Add_Object_Orientation_Disk([0., 0., 0.], [0., 0., 0.], | |
1332 | + vtk_colors.GetColor3d('Red')) | |
1333 | + #self.obj_projection_arrow_actor.SetVisibility(False) | |
1334 | + #self.object_orientation_disk_actor.SetVisibility(False) | |
1323 | 1335 | self.ren.AddActor(self.obj_actor) |
1324 | 1336 | self.ren.AddActor(self.x_actor) |
1325 | 1337 | self.ren.AddActor(self.y_actor) |
1326 | 1338 | self.ren.AddActor(self.z_actor) |
1327 | - | |
1339 | + #self.ren.AddActor(self.obj_projection_arrow_actor) | |
1340 | + #self.ren.AddActor(self.object_orientation_disk_actor) | |
1328 | 1341 | # self.obj_axes = vtk.vtkAxesActor() |
1329 | 1342 | # self.obj_axes.SetShaftTypeToCylinder() |
1330 | 1343 | # self.obj_axes.SetXAxisLabelText("x") |
... | ... | @@ -1334,6 +1347,63 @@ class Viewer(wx.Panel): |
1334 | 1347 | |
1335 | 1348 | # self.ren.AddActor(self.obj_axes) |
1336 | 1349 | |
1350 | + def Add_Object_Orientation_Disk(self, position, orientation, color=[0.0, 0.0, 1.0]): | |
1351 | + # Create a disk to show target | |
1352 | + disk = vtk.vtkDiskSource() | |
1353 | + disk.SetInnerRadius(5) | |
1354 | + disk.SetOuterRadius(15) | |
1355 | + disk.SetRadialResolution(100) | |
1356 | + disk.SetCircumferentialResolution(100) | |
1357 | + disk.Update() | |
1358 | + | |
1359 | + disk_mapper = vtk.vtkPolyDataMapper() | |
1360 | + disk_mapper.SetInputData(disk.GetOutput()) | |
1361 | + disk_actor = vtk.vtkActor() | |
1362 | + disk_actor.SetMapper(disk_mapper) | |
1363 | + disk_actor.GetProperty().SetColor(color) | |
1364 | + disk_actor.GetProperty().SetOpacity(1) | |
1365 | + disk_actor.SetPosition(position) | |
1366 | + disk_actor.SetOrientation(orientation) | |
1367 | + | |
1368 | + return disk_actor | |
1369 | + | |
1370 | + def Add_ObjectArrow(self, direction, orientation, color=[0.0, 0.0, 1.0], size=2): | |
1371 | + vtk_colors = vtk.vtkNamedColors() | |
1372 | + | |
1373 | + arrow = vtk.vtkArrowSource() | |
1374 | + | |
1375 | + mapper = vtk.vtkPolyDataMapper() | |
1376 | + mapper.SetInputConnection(arrow.GetOutputPort()) | |
1377 | + | |
1378 | + actor = vtk.vtkActor() | |
1379 | + actor.SetMapper(mapper) | |
1380 | + actor.GetProperty().SetColor(color) | |
1381 | + actor.GetProperty().SetLineWidth(5) | |
1382 | + actor.AddPosition(0, 0, 0) | |
1383 | + actor.SetScale(size) | |
1384 | + actor.SetPosition(direction) | |
1385 | + actor.SetOrientation(orientation) | |
1386 | + | |
1387 | + return actor | |
1388 | + | |
1389 | + def ObjectArrowLocation(self, m_img, coord): | |
1390 | + # m_img[:3, 0] is from posterior to anterior direction of the coil | |
1391 | + # m_img[:3, 1] is from left to right direction of the coil | |
1392 | + # m_img[:3, 2] is from bottom to up direction of the coil | |
1393 | + vec_length = 70 | |
1394 | + m_img_flip = m_img.copy() | |
1395 | + m_img_flip[1, -1] = -m_img_flip[1, -1] | |
1396 | + p1 = m_img_flip[:-1, -1] # coil center | |
1397 | + coil_dir = m_img_flip[:-1, 0] | |
1398 | + coil_face = m_img_flip[:-1, 1] | |
1399 | + | |
1400 | + coil_norm = np.cross(coil_dir, coil_face) | |
1401 | + p2_norm = p1 - vec_length * coil_norm # point normal to the coil away from the center by vec_length | |
1402 | + coil_dir = np.array([coord[3], coord[4], coord[5]]) | |
1403 | + | |
1404 | + return coil_dir, p2_norm, coil_norm, p1 | |
1405 | + | |
1406 | + | |
1337 | 1407 | def add_line(self, p1, p2, color=[0.0, 0.0, 1.0]): |
1338 | 1408 | line = vtk.vtkLineSource() |
1339 | 1409 | line.SetPoint1(p1) |
... | ... | @@ -1357,6 +1427,80 @@ class Viewer(wx.Panel): |
1357 | 1427 | self.actor_peel = actor |
1358 | 1428 | self.Refresh() |
1359 | 1429 | |
1430 | + def GetPeelCenters(self, centers, normals): | |
1431 | + self.peel_centers = centers | |
1432 | + self.peel_normals = normals | |
1433 | + | |
1434 | + self.Refresh() | |
1435 | + | |
1436 | + def Initlocator_viewer(self, locator): | |
1437 | + self.locator = locator | |
1438 | + self.Refresh() | |
1439 | + | |
1440 | + def GetCellIntersection(self, p1, p2, coil_norm, coil_dir): | |
1441 | + | |
1442 | + vtk_colors = vtk.vtkNamedColors() | |
1443 | + # This find store the triangles that intersect the coil's normal | |
1444 | + intersectingCellIds = vtk.vtkIdList() | |
1445 | + | |
1446 | + #for debugging | |
1447 | + self.x_actor = self.add_line(p1,p2,vtk_colors.GetColor3d('Blue')) | |
1448 | + #self.ren.AddActor(self.x_actor) # remove comment for testing | |
1449 | + | |
1450 | + self.locator.FindCellsAlongLine(p1, p2, .001, intersectingCellIds) | |
1451 | + | |
1452 | + closestDist = 50 | |
1453 | + | |
1454 | + #if find intersection , calculate angle and add actors | |
1455 | + if intersectingCellIds.GetNumberOfIds() != 0: | |
1456 | + for i in range(intersectingCellIds.GetNumberOfIds()): | |
1457 | + cellId = intersectingCellIds.GetId(i) | |
1458 | + point = np.array(self.peel_centers.GetPoint(cellId)) | |
1459 | + distance = np.linalg.norm(point - p1) | |
1460 | + | |
1461 | + #print('distance:', distance, point - p1) | |
1462 | + self.ren.RemoveActor(self.y_actor) | |
1463 | + | |
1464 | + if distance < closestDist: | |
1465 | + closestDist = distance | |
1466 | + closestPoint = point | |
1467 | + pointnormal = np.array(self.peel_normals.GetTuple(cellId)) | |
1468 | + angle = np.rad2deg(np.arccos(np.dot(pointnormal, coil_norm))) | |
1469 | + #print('the angle:', angle) | |
1470 | + | |
1471 | + #for debbuging | |
1472 | + self.y_actor = self.add_line(closestPoint, closestPoint + 75 * pointnormal, | |
1473 | + vtk_colors.GetColor3d('Yellow')) | |
1474 | + | |
1475 | + #self.ren.AddActor(self.y_actor)# remove comment for testing | |
1476 | + | |
1477 | + | |
1478 | + self.ren.AddActor(self.obj_projection_arrow_actor) | |
1479 | + self.ren.AddActor(self.object_orientation_disk_actor) | |
1480 | + | |
1481 | + self.obj_projection_arrow_actor.SetPosition(closestPoint) | |
1482 | + self.obj_projection_arrow_actor.SetOrientation(coil_dir) | |
1483 | + | |
1484 | + self.object_orientation_disk_actor.SetPosition(closestPoint) | |
1485 | + self.object_orientation_disk_actor.SetOrientation(coil_dir) | |
1486 | + | |
1487 | + # change color of arrow and disk according to angle | |
1488 | + if angle < self.angle_arrow_projection_threshold: | |
1489 | + self.object_orientation_disk_actor.GetProperty().SetColor(vtk_colors.GetColor3d('Green')) | |
1490 | + self.obj_projection_arrow_actor.GetProperty().SetColor(vtk_colors.GetColor3d('Green')) | |
1491 | + else: | |
1492 | + self.object_orientation_disk_actor.GetProperty().SetColor(vtk_colors.GetColor3d('indian_red')) | |
1493 | + self.obj_projection_arrow_actor.GetProperty().SetColor(vtk_colors.GetColor3d('indian_red')) | |
1494 | + else: | |
1495 | + self.ren.RemoveActor(self.y_actor) | |
1496 | + | |
1497 | + else: | |
1498 | + self.ren.RemoveActor(self.obj_projection_arrow_actor) | |
1499 | + self.ren.RemoveActor(self.object_orientation_disk_actor) | |
1500 | + self.ren.RemoveActor(self.x_actor) | |
1501 | + #self.ren.RemoveActor(self.y_actor) | |
1502 | + self.Refresh() | |
1503 | + | |
1360 | 1504 | def OnNavigationStatus(self, nav_status, vis_status): |
1361 | 1505 | self.nav_status = nav_status |
1362 | 1506 | self.tracts_status = vis_status[1] |
... | ... | @@ -1368,7 +1512,8 @@ class Viewer(wx.Panel): |
1368 | 1512 | self.x_actor.SetVisibility(self.obj_state) |
1369 | 1513 | self.y_actor.SetVisibility(self.obj_state) |
1370 | 1514 | self.z_actor.SetVisibility(self.obj_state) |
1371 | - | |
1515 | + #self.object_orientation_disk_actor.SetVisibility(self.obj_state) | |
1516 | + #self.obj_projection_arrow_actor.SetVisibility(self.obj_state) | |
1372 | 1517 | self.Refresh() |
1373 | 1518 | |
1374 | 1519 | def UpdateSeedOffset(self, data): |
... | ... | @@ -1419,6 +1564,19 @@ class Viewer(wx.Panel): |
1419 | 1564 | |
1420 | 1565 | self.Refresh() |
1421 | 1566 | |
1567 | + | |
1568 | + def UpdateObjectArrowOrientation(self, m_img, coord, flag): | |
1569 | + | |
1570 | + [coil_dir, norm, coil_norm, p1 ]= self.ObjectArrowLocation(m_img,coord) | |
1571 | + | |
1572 | + if flag: | |
1573 | + self.ren.RemoveActor(self.x_actor) | |
1574 | + #self.ren.RemoveActor(self.y_actor) | |
1575 | + self.ren.RemoveActor(self.obj_projection_arrow_actor) | |
1576 | + self.ren.RemoveActor(self.object_orientation_disk_actor) | |
1577 | + self.GetCellIntersection(p1, norm, coil_norm, coil_dir) | |
1578 | + self.Refresh() | |
1579 | + | |
1422 | 1580 | def UpdateTrackObjectState(self, evt=None, flag=None, obj_name=None, polydata=None): |
1423 | 1581 | if flag: |
1424 | 1582 | self.obj_name = obj_name |
... | ... | @@ -1432,11 +1590,15 @@ class Viewer(wx.Panel): |
1432 | 1590 | self.ren.RemoveActor(self.y_actor) |
1433 | 1591 | self.ren.RemoveActor(self.z_actor) |
1434 | 1592 | self.ren.RemoveActor(self.mark_actor) |
1593 | + self.ren.RemoveActor(self.obj_projection_arrow_actor) | |
1594 | + self.ren.RemoveActor(self.object_orientation_disk_actor) | |
1435 | 1595 | self.obj_actor = None |
1436 | 1596 | self.x_actor = None |
1437 | 1597 | self.y_actor = None |
1438 | 1598 | self.z_actor = None |
1439 | 1599 | self.mark_actor = None |
1600 | + self.obj_projection_arrow_actor = None | |
1601 | + self.object_orientation_disk_actor=None | |
1440 | 1602 | self.Refresh() |
1441 | 1603 | |
1442 | 1604 | def UpdateShowObjectState(self, state): | ... | ... |
invesalius/gui/task_navigator.py
... | ... | @@ -326,6 +326,7 @@ class Navigation(): |
326 | 326 | self.trekker = None |
327 | 327 | self.n_threads = None |
328 | 328 | self.view_tracts = False |
329 | + self.peel_loaded = False | |
329 | 330 | self.enable_act = False |
330 | 331 | self.act_data = None |
331 | 332 | self.n_tracts = const.N_TRACTS |
... | ... | @@ -363,7 +364,7 @@ class Navigation(): |
363 | 364 | if self.event.is_set(): |
364 | 365 | self.event.clear() |
365 | 366 | |
366 | - vis_components = [self.trigger_state, self.view_tracts] | |
367 | + vis_components = [self.trigger_state, self.view_tracts, self.peel_loaded] | |
367 | 368 | vis_queues = [self.coord_queue, self.trigger_queue, self.tracts_queue, self.icp_queue] |
368 | 369 | |
369 | 370 | Publisher.sendMessage("Navigation status", nav_status=True, vis_status=vis_components) |
... | ... | @@ -458,7 +459,7 @@ class Navigation(): |
458 | 459 | self.tracts_queue.clear() |
459 | 460 | self.tracts_queue.join() |
460 | 461 | |
461 | - vis_components = [self.trigger_state, self.view_tracts] | |
462 | + vis_components = [self.trigger_state, self.view_tracts, self.peel_loaded] | |
462 | 463 | Publisher.sendMessage("Navigation status", nav_status=False, vis_status=vis_components) |
463 | 464 | |
464 | 465 | |
... | ... | @@ -870,6 +871,7 @@ class NeuronavigationPanel(wx.Panel): |
870 | 871 | Publisher.subscribe(self.UpdateSleep, 'Update sleep') |
871 | 872 | Publisher.subscribe(self.UpdateNumberThreads, 'Update number of threads') |
872 | 873 | Publisher.subscribe(self.UpdateTractsVisualization, 'Update tracts visualization') |
874 | + Publisher.subscribe(self.UpdatePeelVisualization, 'Update peel visualization') | |
873 | 875 | Publisher.subscribe(self.EnableACT, 'Enable ACT') |
874 | 876 | Publisher.subscribe(self.UpdateACTData, 'Update ACT data') |
875 | 877 | Publisher.subscribe(self.UpdateNavigationStatus, 'Navigation status') |
... | ... | @@ -918,6 +920,10 @@ class NeuronavigationPanel(wx.Panel): |
918 | 920 | self.ResetICP() |
919 | 921 | self.tracker.UpdateUI(self.select_tracker_elem, self.numctrls_coord[3:6], self.txtctrl_fre) |
920 | 922 | |
923 | + | |
924 | + def UpdatePeelVisualization(self, data): | |
925 | + self.navigation.peel_loaded = data | |
926 | + | |
921 | 927 | def UpdateNavigationStatus(self, nav_status, vis_status): |
922 | 928 | self.nav_status = nav_status |
923 | 929 | if nav_status and self.icp.m_icp is not None: |
... | ... | @@ -1044,6 +1050,7 @@ class NeuronavigationPanel(wx.Panel): |
1044 | 1050 | self.navigation.UpdateFiducialRegistrationError(self.tracker) |
1045 | 1051 | fre, fre_ok = self.navigation.GetFiducialRegistrationError(self.icp) |
1046 | 1052 | |
1053 | + | |
1047 | 1054 | self.txtctrl_fre.SetValue(str(round(fre, 2))) |
1048 | 1055 | if fre_ok: |
1049 | 1056 | self.txtctrl_fre.SetBackgroundColour('GREEN') |
... | ... | @@ -1862,7 +1869,7 @@ class TractographyPanel(wx.Panel): |
1862 | 1869 | self.tracts_run = None |
1863 | 1870 | self.trekker_cfg = const.TREKKER_CONFIG |
1864 | 1871 | self.nav_status = False |
1865 | - | |
1872 | + self.peel_loaded = False | |
1866 | 1873 | self.SetAutoLayout(1) |
1867 | 1874 | self.__bind_events() |
1868 | 1875 | |
... | ... | @@ -2040,9 +2047,12 @@ class TractographyPanel(wx.Panel): |
2040 | 2047 | def OnSelectPeelingDepth(self, evt, ctrl): |
2041 | 2048 | self.peel_depth = ctrl.GetValue() |
2042 | 2049 | if self.checkpeeling.GetValue(): |
2043 | - actor = self.brain_peel.get_actor(self.peel_depth) | |
2050 | + actor = self.brain_peel.get_actor(self.peel_depth, self.affine_vtk) | |
2044 | 2051 | Publisher.sendMessage('Update peel', flag=True, actor=actor) |
2045 | - | |
2052 | + Publisher.sendMessage('Get peel centers and normals', centers=self.brain_peel.peel_centers, | |
2053 | + normals=self.brain_peel.peel_normals) | |
2054 | + Publisher.sendMessage('Get init locator', locator=self.brain_peel.locator) | |
2055 | + self.peel_loaded = True | |
2046 | 2056 | def OnSelectNumTracts(self, evt, ctrl): |
2047 | 2057 | self.n_tracts = ctrl.GetValue() |
2048 | 2058 | # self.tract.n_tracts = ctrl.GetValue() |
... | ... | @@ -2070,7 +2080,7 @@ class TractographyPanel(wx.Panel): |
2070 | 2080 | def OnShowPeeling(self, evt, ctrl): |
2071 | 2081 | # self.view_peeling = ctrl.GetValue() |
2072 | 2082 | if ctrl.GetValue(): |
2073 | - actor = self.brain_peel.get_actor(self.peel_depth) | |
2083 | + actor = self.brain_peel.get_actor(self.peel_depth, self.affine_vtk) | |
2074 | 2084 | else: |
2075 | 2085 | actor = None |
2076 | 2086 | Publisher.sendMessage('Update peel', flag=ctrl.GetValue(), actor=actor) |
... | ... | @@ -2114,13 +2124,18 @@ class TractographyPanel(wx.Panel): |
2114 | 2124 | |
2115 | 2125 | try: |
2116 | 2126 | self.brain_peel = brain.Brain(img_path, mask_path, self.n_peels, self.affine_vtk) |
2117 | - self.brain_actor = self.brain_peel.get_actor(self.peel_depth) | |
2127 | + self.brain_actor = self.brain_peel.get_actor(self.peel_depth, self.affine_vtk) | |
2118 | 2128 | self.brain_actor.GetProperty().SetOpacity(self.brain_opacity) |
2119 | 2129 | Publisher.sendMessage('Update peel', flag=True, actor=self.brain_actor) |
2130 | + Publisher.sendMessage('Get peel centers and normals', centers=self.brain_peel.peel_centers, | |
2131 | + normals=self.brain_peel.peel_normals) | |
2132 | + Publisher.sendMessage('Get init locator', locator=self.brain_peel.locator) | |
2120 | 2133 | self.checkpeeling.Enable(1) |
2121 | 2134 | self.checkpeeling.SetValue(True) |
2122 | 2135 | self.spin_opacity.Enable(1) |
2123 | 2136 | Publisher.sendMessage('Update status text in GUI', label=_("Brain model loaded")) |
2137 | + self.peel_loaded = True | |
2138 | + Publisher.sendMessage('Update peel visualization', data= self.peel_loaded) | |
2124 | 2139 | except: |
2125 | 2140 | wx.MessageBox(_("Unable to load brain mask."), _("InVesalius 3")) |
2126 | 2141 | |
... | ... | @@ -2308,7 +2323,7 @@ class UpdateNavigationScene(threading.Thread): |
2308 | 2323 | """ |
2309 | 2324 | |
2310 | 2325 | threading.Thread.__init__(self, name='UpdateScene') |
2311 | - self.trigger_state, self.view_tracts = vis_components | |
2326 | + self.trigger_state, self.view_tracts, self.peel_loaded = vis_components | |
2312 | 2327 | self.coord_queue, self.trigger_queue, self.tracts_queue, self.icp_queue = vis_queues |
2313 | 2328 | self.sle = sle |
2314 | 2329 | self.event = event |
... | ... | @@ -2347,7 +2362,7 @@ class UpdateNavigationScene(threading.Thread): |
2347 | 2362 | |
2348 | 2363 | if view_obj: |
2349 | 2364 | wx.CallAfter(Publisher.sendMessage, 'Update object matrix', m_img=m_img, coord=coord) |
2350 | - | |
2365 | + wx.CallAfter(Publisher.sendMessage, 'Update object arrow matrix',m_img=m_img, coord=coord, flag= self.peel_loaded) | |
2351 | 2366 | self.coord_queue.task_done() |
2352 | 2367 | # print('UpdateScene: done {}'.format(count)) |
2353 | 2368 | # count += 1 | ... | ... |