# Create a plane that is perpendicular to another plane

**URL:** <https://discourse.slicer.org/t/create-a-plane-that-is-perpendicular-to-another-plane/47307>\
**Category:** Support\
**Created:** [June 11, 2026, 1:30am UTC](https://discourse.slicer.org/t/create-a-plane-that-is-perpendicular-to-another-plane/47307 "2026-06-11T01:30:51Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![jsguerra](https://avatars.discourse-cdn.com/v4/letter/j/919ad9/32.png) [@jsguerra](https://discourse.slicer.org/u/jsguerra)\
**Post date:** [June 11, 2026, 1:30am UTC](https://discourse.slicer.org/t/create-a-plane-that-is-perpendicular-to-another-plane/47307/1 "2026-06-11T01:30:51Z")

</div>

I’ve created a best fit plane from a point list in the Markups module and I would like to create a second plane perpendicular to the best fit plane. Attached is an example using one of the mouse crania from the SlicerMorph sample data.

 ![example plane](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/6/6/66aebfd139c911afb3b74b05d30295140cc17683.jpeg)

Here, I created a best fit plane (Plane A) using landmarks 13,14,15,28,54, and 55. I would like to create a second perpendicular plane (Plane B) so that Plane A represents the X-axis and Plane B represents a Y-Axis.

I looked over the documentation and scripts repository but I’m also not well versed in python so I’m not sure what to look for. Any help would be appreciated.

---

<div class="post-metadata">

**Author:** ![ThomasVanParys](https://avatars.discourse-cdn.com/v4/letter/t/7ea924/32.png) [@ThomasVanParys](https://discourse.slicer.org/u/ThomasVanParys)\
**Post date:** [June 11, 2026, 9:25am UTC](https://discourse.slicer.org/t/create-a-plane-that-is-perpendicular-to-another-plane/47307/2 "2026-06-11T09:25:41Z")

</div>

Could you not also set Plane B using the Markups module? You only need two landmarks. I think you can set the plane normal constrained relative to Plane A or the plane origin at a landmark/centroid?

---

<div class="post-metadata">

**Author:** ![Philippe\_Pellerin](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/philippe_pellerin/32/12877_2.png) [@Philippe\_Pellerin](https://discourse.slicer.org/u/Philippe_Pellerin)\
**Post date:** [June 11, 2026, 12:40pm UTC](https://discourse.slicer.org/t/create-a-plane-that-is-perpendicular-to-another-plane/47307/3 "2026-06-11T12:40:25Z")

</div>

It is very easy, have a look at this tutorial to see how to create orthogonal planning.[https://youtu.be/Bs4GWLrFQfA](https://youtu.be/Bs4GWLrFQfA)

---

<div class="post-metadata">

**Author:** ![jsguerra](https://avatars.discourse-cdn.com/v4/letter/j/919ad9/32.png) [@jsguerra](https://discourse.slicer.org/u/jsguerra)\
**Post date:** [June 11, 2026, 1:29pm UTC](https://discourse.slicer.org/t/create-a-plane-that-is-perpendicular-to-another-plane/47307/4 "2026-06-11T13:29:54Z")

</div>

Yes, the idea would be to add a second plane in the Markups module, but where I’m having issues is figuring out how to constrain it to be perpendicular relative to plane A.

---

<div class="post-metadata">

**Author:** ![chir.set](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/chir.set/32/66982_2.png) [@chir.set](https://discourse.slicer.org/u/chir.set)\
**Post date:** [June 11, 2026, 2:47pm UTC](https://discourse.slicer.org/t/create-a-plane-that-is-perpendicular-to-another-plane/47307/5 "2026-06-11T14:47:01Z")

</div>

If you don’t want to use python scripting, you may consider the Reslice functions in [this](https://gitlab.com/chir-set/Tools7/-/tree/master/ManyThingsToolBar?ref_type=heads) project.

- Reslice a view be perpendicular to that plane (choose Binormal or Tangent).
- Place a second plane markups node in the latter slice view.
- Restore the orientation of the slice view.

---

<div class="post-metadata">

**Author:** ![jsguerra](https://avatars.discourse-cdn.com/v4/letter/j/919ad9/32.png) [@jsguerra](https://discourse.slicer.org/u/jsguerra)\
**Post date:** [June 11, 2026, 5:49pm UTC](https://discourse.slicer.org/t/create-a-plane-that-is-perpendicular-to-another-plane/47307/7 "2026-06-11T17:49:31Z")

</div>

@chir.set Thanks for sharing. I installed the tool but I’m only working with surface PLY files, so I dont have slices.

Also, I’m not opposed to using python, I just don’t know how to use python but I’m willing to learn.

---

<div class="post-metadata">

**Author:** ![jsguerra](https://avatars.discourse-cdn.com/v4/letter/j/919ad9/32.png) [@jsguerra](https://discourse.slicer.org/u/jsguerra)\
**Post date:** [June 11, 2026, 5:56pm UTC](https://discourse.slicer.org/t/create-a-plane-that-is-perpendicular-to-another-plane/47307/8 "2026-06-11T17:56:39Z")

</div>

Thank you for sharing. That is quite easy however I’m only working with surface PLY files, not CT/DICOM files.

---

<div class="post-metadata">

**Author:** ![chir.set](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/chir.set/32/66982_2.png) [@chir.set](https://discourse.slicer.org/u/chir.set)\
**Post date:** [June 11, 2026, 6:27pm UTC](https://discourse.slicer.org/t/create-a-plane-that-is-perpendicular-to-another-plane/47307/9 "2026-06-11T18:27:24Z")

</div>

You don’t need a volume for that. The slice views can be empty. You don’t even need a model.

 ![NoVolumeNeeded](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/6/4/649f4227c2384ce42e43c75b7bb3fb74b5af5126.png)

---

<div class="post-metadata">

**Author:** ![jsguerra](https://avatars.discourse-cdn.com/v4/letter/j/919ad9/32.png) [@jsguerra](https://discourse.slicer.org/u/jsguerra)\
**Post date:** [June 12, 2026, 1:33am UTC](https://discourse.slicer.org/t/create-a-plane-that-is-perpendicular-to-another-plane/47307/10 "2026-06-12T01:33:25Z")

</div>

@chir.set Got it, thanks for the clarification. I was using a 3D only view and was confused why nothing seemed to be happening. Just tried it again and it works remarkably well!

 ![perpendicular planes](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/9/a/9aeca4c2e170171dfc6d79948374d365fc8aebee.jpeg)

I did notice that because the my surface is not aligned along the axis, I had to rotate the new plane created (Plane B; Green) along the one axis to get it to run along the direction I’m interested in (Plane B - Rotated; Blue). Is there a way to accomplish this without having to manually rotate it?

---

<div class="post-metadata">

**Author:** ![chir.set](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/chir.set/32/66982_2.png) [@chir.set](https://discourse.slicer.org/u/chir.set)\
**Post date:** [June 12, 2026, 8:59am UTC](https://discourse.slicer.org/t/create-a-plane-that-is-perpendicular-to-another-plane/47307/11 "2026-06-12T08:59:42Z")

</div>

> [@jsguerra](#):
>
> Is there a way to accomplish this without having to manually rotate it?

You can proceed this way:

- Check ‘Orthogonal intersection’ when you orient a slice view to be parallel to your first plane node (use Normal).
- Activate ‘Slice intersections’ in the slice view (right click).
- Rotate the other slice views in it, by ‘Ctrl+Alt+Mouse move’ or by checking ‘Interaction’ to activate the handles; rotate so that one of the views is as you want (see the line colour in the slice view).
- In the target slice view (line colour in the source slice view), place a new plane markups node.
- Optionally restore the orientation slice views.

---

<div class="post-metadata">

**Author:** ![jsguerra](https://avatars.discourse-cdn.com/v4/letter/j/919ad9/32.png) [@jsguerra](https://discourse.slicer.org/u/jsguerra)\
**Post date:** [June 16, 2026, 4:36pm UTC](https://discourse.slicer.org/t/create-a-plane-that-is-perpendicular-to-another-plane/47307/12 "2026-06-16T16:36:24Z")

</div>

> [@chir.set](#):
>
> You can proceed this way:
> 
> - Check ‘Orthogonal intersection’ when you orient a slice view to be parallel to your first plane node (use Normal).
> - Activate ‘Slice intersections’ in the slice view (right click).
> - Rotate the other slice views in it, by ‘Ctrl+Alt+Mouse move’ or by checking ‘Interaction’ to activate the handles; rotate so that one of the views is as you want (see the line colour in the slice view).
> - In the target slice view (line colour in the source slice view), place a new plane markups node.
> - Optionally restore the orientation slice views

This worked perfectly, thank you!

---

<div class="post-metadata">

**Author:** ![chz31](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/chz31/32/77363_2.png) [@chz31](https://discourse.slicer.org/u/chz31)\
**Post date:** [June 17, 2026, 5:17pm UTC](https://discourse.slicer.org/t/create-a-plane-that-is-perpendicular-to-another-plane/47307/13 "2026-06-17T17:17:21Z")

</div>

I have a helper script that might be helpful.

```auto
################# Create points at 4, 7, 10mm
import numpy as np

Given points

P0 = np.array([-95.807, 86.787, -0.063])
P1 = np.array([-96.027, 87.707, -4.015])

Direction vector

v = P1 - P0

Normalize

v_hat = v / np.linalg.norm(v)

Distances you want (mm)

distances = [4, 7, 10]

print("Unit direction vector:", v_hat, "\n")

for d in distances:
P = P0 + d * v_hat
print(f"Point at {d} mm:", P)

#####Create horizontal planes at 4, 7, 10mm
def createPlanes(f, plane2Center=None, angle=None, useBinormal=True,
plane1Name="Plane1", plane2Name="Plane_7mm"):
"""
f: vtkMRMLMarkupsFiducialNode (or any markups node with >=3 control points)
plane2Center: [x,y,z] world coords where plane2 passes through (default: p1)
angle: rotation (degrees) to spin perpendicular choice around plane1 normal (default: 0)
useBinormal: True -> use binormal as plane2 normal, False -> use tangent
"""

# --- Read 3 points from markups node f ---
p1 = [0.0, 0.0, 0.0]
p2 = [0.0, 0.0, 0.0]
p3 = [0.0, 0.0, 0.0]
f.GetNthControlPointPositionWorld(0, p1)
f.GetNthControlPointPositionWorld(1, p2)
f.GetNthControlPointPositionWorld(2, p3)

rotationOfPerpendiculars = float(angle) if angle is not None else 0.0

plane2Coordinates = plane2Center if plane2Center is not None else p1

# --- Create Plane1 (3-point plane) ---
plane1 = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsPlaneNode", plane1Name)
plane1.CreateDefaultDisplayNodes()
plane1.SetPlaneType(slicer.vtkMRMLMarkupsPlaneNode.PlaneType3Points)
plane1.AddControlPointWorld(p1)
plane1.AddControlPointWorld(p2)
plane1.AddControlPointWorld(p3)

# --- Get Plane1 normal (world) ---
plane1Normal = [0.0, 0.0, 0.0]
plane1.GetNormalWorld(plane1Normal)
vtk.vtkMath.Normalize(plane1Normal)

# --- Compute perpendicular directions to plane1Normal ---
plane1Binormal = [0.0, 0.0, 0.0]
plane1Tangent = [0.0, 0.0, 0.0]
vtk.vtkMath.Perpendiculars(plane1Normal, plane1Binormal, plane1Tangent, rotationOfPerpendiculars)

n2 = plane1Binormal if useBinormal else plane1Tangent
vtk.vtkMath.Normalize(n2)

# --- Create Plane2 (point + normal) ---
plane2 = slicer.mrmlScene.AddNewNodeByClass("vtkMRMLMarkupsPlaneNode", plane2Name)
plane2.CreateDefaultDisplayNodes()
plane2.AddControlPointWorld(plane2Coordinates)
plane2.SetNormalWorld(n2)

print("Plane1 points:", p1, p2, p3)
print("Plane1 normal:", plane1Normal)
print("Plane2 origin:", plane2Coordinates)
print("Plane2 normal:", n2)

return plane1, plane2

f = slicer.util.getNode("F_1") # replace "F" with your fiducials node name
plane1, plane2 = createPlanes(
f,
plane2Center=[-96.348, 89.051, -9.788],
angle=0.0,
useBinormal=False
)
#useBinormal = True create another perpendicular plane

```

This is for another work so the naming and some parts were a bit confusing. You could pretty much ignore lines 1-23.

You need to first create a fiducial markup `F_1` or your preferred name (you need to change line 82 `getNode("F_1")`) with three points to define a plane. You can place the three points along your mid-sagittal plane. It will create a plane called `Plane`

Then you need to define `plane2Center=[-96.348, 89.051, -9.788]`at line 85. You can probably switch the numbers to the coordinates of your sagittal plane’s center.

It will create a perpendicular plane named as `Plane_7mm` according to the normal of `plane1.GetNormalWorld(plane1Normal)`.

 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/4/b/4bd119cba817bf51d4c5b8b54c337c1a621a9e1b.jpeg)

Hopefully it works for your case.
