Create plane on 3D model - problem with landmarks

Good Day. I want to create a plane on the 3D model of the calcaneus. I installed the module “Angle planes”. According to the guide, I click the “add landmark” button and try to manually place landmarks. But instead, landmarks are created on the surface of the model automatically in large quantities (20-30). At first, I thought that the problem was specifically in the program on my computer and installed the 3D slicer on my laptop. But the same thing happens there. Please help me solve the problem.

I don’t think the AnglePlanes extension is maintained. You can submit an issue to its developers at Issues · DCBIA-OrthoLab/AnglePlanes-Extension · GitHub.

Fortunately, this extension is almost fully replaced by Markups module.
You can place a “plane” markup on the model directly, using Markups module. Markups module does not provide relative measurements between multiple markups but you can specify any number of automatic relative measurements by a few lines of Python script. For example, you can copy-paste this code snippet to get the angle between plane nodes P and P_1:

plane1 = getNode("P")
plane2 = getNode("P_1")
angleDeg = vtk.vtkMath.DegreesFromRadians(vtk.vtkMath.AngleBetweenVectors(plane1.GetNormalWorld(), plane2.GetNormalWorld()))
print(f"Angle between planes: {angleDeg:0.1f}")

A bit more sophisticated script that continuously prints the current angle in ths script repository.

Thanks for the answer. I still wanted to automate the process of measuring the angle between planes, because my task is to measure orthopedic angles by four points. Using the command line complicates the process too much.

Professor Lasso, сan I transfer the value of the obtained angle between two planes from console to the nodes section?

GUI used to be simpler. However, for this kind of custom measurement tasks, I think using scripting is now simpler and more efficient.

The most important thing that you might not know that for simple tasks like this, you don’t need to write program code anymore. Instead, you can ask any AI chatbot (ChatGPT, Microsoft Copilot, etc. all know 3D Slicer well) and it does everything for you.

For example, I used the free Microsoft Copilot. I asked:

Generate a code snippet that I can copy into 3D Slicer that measures the angle between planes "P" and "P_1" and displays the value in a popup window. Run this measurement when I hit “Ctrl-A”.

It generated code that worked perfectly:

Code snippet generated by Microsoft Copilot
import slicer
from qt import QKeySequence, QShortcut, QMessageBox

def measure_angle_between_planes():
    # Get the plane nodes
    plane1 = slicer.mrmlScene.GetFirstNodeByName('P')
    plane2 = slicer.mrmlScene.GetFirstNodeByName('P_1')
    
    if not plane1 or not plane2:
        QMessageBox.warning(slicer.util.mainWindow(), 'Error', 'One or both planes not found.')
        return

    # Get the normal vectors of the planes
    normal1 = [0, 0, 0]
    normal2 = [0, 0, 0]
    plane1.GetNormal(normal1)
    plane2.GetNormal(normal2)

    # Calculate the angle between the normals
    import numpy as np
    dot_product = np.dot(normal1, normal2)
    norm1 = np.linalg.norm(normal1)
    norm2 = np.linalg.norm(normal2)
    angle_radians = np.arccos(dot_product / (norm1 * norm2))
    angle_degrees = np.degrees(angle_radians)

    # Display the angle in a popup window
    QMessageBox.information(slicer.util.mainWindow(), 'Angle Between Planes', f'The angle between the planes is: {angle_degrees:.2f} degrees')

# Create a shortcut for Ctrl-A to run the measurement
shortcut = QShortcut(QKeySequence("Ctrl+A"), slicer.util.mainWindow())
shortcut.activated.connect(measure_angle_between_planes)

I copy-pasted this code to the Python console, then placed two planes, then hit Ctrl-A, and it showed the angle value in a popup window, as I asked.

If you don’t want to copy-paste this code each time you start Slicer, you can put it in the Application startup script (you can get the location of this file from the application menu → Edit → Application settings → General → Application startup script).

The beauty of this is that you don’t rely on any developer to do anything for you, you don’t need to know any programming (other than maybe basic syntax and basic math so that you can verify that the values are computed exactly the way you want it). If you want to write the results to a file instead of seeing it in a popup, you just tell the AI chatbot to do that. If you also want to make other measurements, such as measure distance between some points and the plane, etc., just tell it.

For example, you want to see the angle value in the node name? Let’s ask for it:

Modify the code so that it writes the angle value into the name of the second plane node.

Copilot generated an updated code snippet in 3 seconds, which again works flawlessly (I just changed the shortcut to Ctrl-Q because if you set the same keyboard shortcut to do two different things then it will become ambiguous and so it will be disabled).

Code snippet generated by Microsoft Copilot
import slicer
from qt import QKeySequence, QShortcut, QMessageBox

def measure_angle_between_planes():
    # Get the plane nodes
    plane1 = slicer.mrmlScene.GetFirstNodeByName('P')
    plane2 = slicer.mrmlScene.GetFirstNodeByName('P_1')
    
    if not plane1 or not plane2:
        QMessageBox.warning(slicer.util.mainWindow(), 'Error', 'One or both planes not found.')
        return

    # Get the normal vectors of the planes
    normal1 = [0, 0, 0]
    normal2 = [0, 0, 0]
    plane1.GetNormal(normal1)
    plane2.GetNormal(normal2)

    # Calculate the angle between the normals
    import numpy as np
    dot_product = np.dot(normal1, normal2)
    norm1 = np.linalg.norm(normal1)
    norm2 = np.linalg.norm(normal2)
    angle_radians = np.arccos(dot_product / (norm1 * norm2))
    angle_degrees = np.degrees(angle_radians)

    # Write the angle value into the name of the second plane node
    plane2.SetName(f'P_1 (Angle: {angle_degrees:.2f} degrees)')

    # Display the angle in a popup window
    QMessageBox.information(slicer.util.mainWindow(), 'Angle Between Planes', f'The angle between the planes is: {angle_degrees:.2f} degrees')

# Create a shortcut for Ctrl-A to run the measurement
shortcut = QShortcut(QKeySequence("Ctrl+A"), slicer.util.mainWindow())
shortcut.activated.connect(measure_angle_between_planes)

Professor Lasso, thank you for your detailed answer!

Please let us know how it all worked for you.

If we get positive feedback from you and other users then we may build in an AI assistant directly into Slicer. It would automate the steps that I described above: it would guide users through the process and would do all the copy-pasting of code snippets to the Python console and startup script.

1 Like

Hello, Professor Lasso. I used your advice and encountered new difficulties:

  1. You can use the script to automatically measure the angle between two planes by writing the angle value into the plane name only once. If I want to measure this angle again, nothing will work (because the plane name changes by writing a number).
  2. Is it possible to make it so that each measurement is written directly into the name of the “angle” in the “node”? For example, I measure the angle in the sagittal plane between the tibia (plane “P”) and talus (plane "P_1) and write this value into angle “A”. When measuring the angle in the sagittal plane between the tibia (plane “P”) and calcaneus (plane “P_2”) , the resulting value is written into angle “A_1”. This would be very convenient.
    Thank you in advance for your answer.