Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BoneReconstructionPlanner's virtual surgical planning is now tested #68

Open
mauigna06 opened this issue Dec 15, 2022 · 5 comments
Open
Assignees

Comments

@mauigna06
Copy link
Collaborator

@lassoan could you give feedback about it?

def section_SimulateAndImproveMandibleReconstruction(self):
self.delayDisplay("Starting the SimulateAndImproveMandibleReconstruction")
self.delayDisplay("Create the reconstruction for first time")
self.logicBRP.onGenerateFibulaPlanesTimerTimeout()
self.delayDisplay("Reconstruction successful")
#
# # generate mandibular plane movements with this code:
# def createListFromFolderID(folderID):
# createdList = []
# shNode = slicer.mrmlScene.GetSubjectHierarchyNode()
# myList = vtk.vtkIdList()
# shNode.GetItemChildren(folderID,myList)
# for i in range(myList.GetNumberOfIds()):
# createdList.append(shNode.GetItemDataNode(myList.GetId(i)))
# return createdList
# def updateMandibularPlaneMovementsList(caller=None,event=None,movementsList=[]):
# plane = caller
# planeMatrix = vtk.vtkMatrix4x4()
# plane.GetObjectToWorldMatrix(planeMatrix)
# movementsList.append([plane.GetID(),slicer.util.arrayFromVTKMatrix(planeMatrix).tolist()])
# shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene)
# mandiblePlanesFolder = shNode.GetItemByName("Mandibular planes")
# mandiblePlanes = createListFromFolderID(mandiblePlanesFolder)
# # list to save the movements for the test
# movementsList = []
# # set observers
# planesAndObserversList = []
# for plane in mandiblePlanes:
# planesAndObserversList.append(
# [
# plane.GetID(),
# plane.AddObserver(
# slicer.vtkMRMLMarkupsNode.PointEndInteractionEvent,
# lambda caller,event,movementsList=movementsList: updateMandibularPlaneMovementsList(caller,event,movementsList)
# )
# ]
# )
#
#
if not slicer.app.commandOptions().noMainWindow:
layoutManager = slicer.app.layoutManager()
mandibleViewNode = slicer.mrmlScene.GetSingletonNode(self.logicBRP.MANDIBLE_VIEW_SINGLETON_TAG, "vtkMRMLViewNode")
layoutManager.setMaximizedViewNode(mandibleViewNode)
# 8 movements below
# movementsList = [['vtkMRMLMarkupsPlaneNode4', [[0.5161781920883237, -0.04134258142560255, -0.8554828256449669, -9.862711906433127], [-0.8468593163756161, 0.12466280634486007, -0.5169994999823967, 107.90783691406249], [0.12802098374975024, 0.991337468108258, 0.02933687175645767, -83.94944763183594], [0.0, 0.0, 0.0, 1.0]]], ['vtkMRMLMarkupsPlaneNode4', [[0.516178231664674, -0.041342429710410605, -0.8554828090973949, -7.397708892822266], [-0.8468592958433797, 0.12466287181842768, -0.5169995178272775, 103.8636703491211], [0.12802095999946736, 0.9913374662019095, 0.029337039816463184, -83.33809661865234], [0.0, 0.0, 0.0, 1.0]]], ['vtkMRMLMarkupsPlaneNode2', [[0.8922376682835299, 0.18524939419545736, -0.41181865577725557, -28.11571121215824], [-0.4465787117400004, 0.22681900255851603, -0.8655175297467994, 84.53712463378889], [-0.06692830131275733, 0.9561567873673105, 0.2851048937756292, -79.05013275146474], [0.0, 0.0, 0.0, 1.0]]], ['vtkMRMLMarkupsPlaneNode2', [[0.8922376766572847, 0.1852491791773996, -0.41181873435688954, -24.693025588989258], [-0.4465787239770117, 0.22681897690359318, -0.8655175301560744, 82.82402038574219], [-0.06692810802850067, 0.9561568351115349, 0.2851047790290762, -79.30686950683594], [0.0, 0.0, 0.0, 1.0]]], ['vtkMRMLMarkupsPlaneNode3', [[-0.708383352772158, -0.24065112649761564, -0.6635360282838488, 22.992961883544925], [-0.6972025285352179, 0.09205350357255149, 0.7109393692039546, 100.19924163818364], [-0.11000754392029201, 0.9662366106681359, -0.23299174338413398, -77.51470947265622], [0.0, 0.0, 0.0, 1.0]]], ['vtkMRMLMarkupsPlaneNode3', [[-0.7083834009232749, -0.240651176447713, -0.6635359587623766, 20.943126678466797], [-0.6972024658214877, 0.09205345939667467, 0.7109394364258759, 98.18177032470703], [-0.11000763132079507, 0.9662366024361924, -0.23299173625635644, -77.8330307006836], [0.0, 0.0, 0.0, 1.0]]], ['vtkMRMLMarkupsPlaneNode1', [[-0.8984010895060554, -0.2612400933848085, -0.35302846341708666, 39.90895843505858], [-0.42656108809295407, 0.32777755218344756, 0.8429753937153698, 68.75823974609365], [-0.1045041649853625, 0.9079182176236601, -0.4059105684849661, -64.72950744628893], [0.0, 0.0, 0.0, 1.0]]], ['vtkMRMLMarkupsPlaneNode1', [[-0.8984010923061703, -0.2612399980002971, -0.3530285268754991, 36.00282287597656], [-0.4265610935556299, 0.32777742996859105, 0.8429754384724448, 66.90361022949219], [-0.10450411861599232, 0.9079182891912633, -0.4059104203445687, -65.18387603759766], [0.0, 0.0, 0.0, 1.0]]]]
# 4 movements below
movementsList = [['vtkMRMLMarkupsPlaneNode4', [[0.516178231664674, -0.041342429710410605, -0.8554828090973949, -7.397708892822266], [-0.8468592958433797, 0.12466287181842768, -0.5169995178272775, 103.8636703491211], [0.12802095999946736, 0.9913374662019095, 0.029337039816463184, -83.33809661865234], [0.0, 0.0, 0.0, 1.0]]], ['vtkMRMLMarkupsPlaneNode2', [[0.8922376766572847, 0.1852491791773996, -0.41181873435688954, -24.693025588989258], [-0.4465787239770117, 0.22681897690359318, -0.8655175301560744, 82.82402038574219], [-0.06692810802850067, 0.9561568351115349, 0.2851047790290762, -79.30686950683594], [0.0, 0.0, 0.0, 1.0]]], ['vtkMRMLMarkupsPlaneNode3', [[-0.7083834009232749, -0.240651176447713, -0.6635359587623766, 20.943126678466797], [-0.6972024658214877, 0.09205345939667467, 0.7109394364258759, 98.18177032470703], [-0.11000763132079507, 0.9662366024361924, -0.23299173625635644, -77.8330307006836], [0.0, 0.0, 0.0, 1.0]]], ['vtkMRMLMarkupsPlaneNode1', [[-0.8984010923061703, -0.2612399980002971, -0.3530285268754991, 36.00282287597656], [-0.4265610935556299, 0.32777742996859105, 0.8429754384724448, 66.90361022949219], [-0.10450411861599232, 0.9079182891912633, -0.4059104203445687, -65.18387603759766], [0.0, 0.0, 0.0, 1.0]]]]
for item in movementsList:
self.delayDisplay("Update mandibular plane and reconstruction")
self.delayDisplay("Move mandibular plane")
nodeID = item[0]
newPlaneToWorldMatrix = slicer.util.vtkMatrixFromArray(np.array(item[1]))
planeNode = slicer.mrmlScene.GetNodeByID(nodeID)
oldPlaneToWorld = vtk.vtkMatrix4x4()
planeNode.GetObjectToNodeMatrix(oldPlaneToWorld)
worldToOldPlane = vtk.vtkMatrix4x4()
vtk.vtkMatrix4x4.Invert(oldPlaneToWorld, worldToOldPlane)
transform = vtk.vtkTransform()
transform.PostMultiply()
transform.Concatenate(worldToOldPlane)
transform.Concatenate(newPlaneToWorldMatrix)
wasModified = planeNode.StartModify()
for i in range(3):
oldPos = planeNode.GetNthControlPointPosition(i)
newPos = [0,0,0]
transform.TransformPoint(oldPos,newPos)
planeNode.SetNthControlPointPosition(i,newPos)
planeNode.EndModify(wasModified)
self.delayDisplay("Mandibular plane moved")
#
self.delayDisplay("Update reconstruction")
self.logicBRP.onGenerateFibulaPlanesTimerTimeout()
self.delayDisplay("Update successful")
if not slicer.app.commandOptions().noMainWindow:
layoutManager = slicer.app.layoutManager()
layoutManager.setMaximizedViewNode(None)
self.delayDisplay("SimulateAndImproveMandibleReconstruction test successful")

Does reaching the end of the test section_SimulateAndImproveMandibleReconstruction means that the test should be considered passed? Some assertions on numeric results could be added like fibula-bone-pieces meshes number of points, what do you think?

Here is a video of the workflow tests implemented till now:

2022-12-15.16-05-54.mp4

Regarding using it as instructive material:

  • should we add a button to set developer-mode=True and restart Slicer so the reload&test button is visible?
@mauigna06 mauigna06 self-assigned this Dec 15, 2022
@lassoan
Copy link
Collaborator

lassoan commented Dec 15, 2022

The test passes if it does not raise an exception.

@lassoan
Copy link
Collaborator

lassoan commented Dec 17, 2022

This test is certainly very useful for catching some me errors. It is promising for automatically generating tutorials, but for that it would be important to see mouse pointer and click positions. It would be also better to see the text near the bottom instead in the center of the screen.

@mauigna06
Copy link
Collaborator Author

Thank you for the feedback @lassoan

it would be important to see mouse pointer and click positions

Could you refer me to a code example that does this? How to do it window-size independent? How to create the test data?

It would be also better to see the text near the bottom instead in the center of the screen.

I agree, I'll try to do that

@lassoan
Copy link
Collaborator

lassoan commented Dec 17, 2022

I don't have code example, but you can display a widget (such as QIcon) outside the layout to indicate the mouse position. You can do a small animation (change the size, color, flash it, etc) to indicate clicking. You could generate smooth mouse trajectory between click positions to simulate somewhat realistic mouse motion. Screen capture module can be used to capture screenshots and put them into a mp4 video.

We plan to develop something like this for the Slicer for Latin America project, for automatic creation of multi-lingual training materials (videos, slide shows, documentation pages).

@mauigna06
Copy link
Collaborator Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants