# Projecting points to the positive external surface

**URL:** <https://discourse.slicer.org/t/projecting-points-to-the-positive-external-surface/32593>\
**Category:** Development\
**Tags:** registration\
**Created:** [November 3, 2023, 11:35pm UTC](https://discourse.slicer.org/t/projecting-points-to-the-positive-external-surface/32593 "2023-11-03T23:35:16Z")\
**Posts on this page:** 7\
**Page:** 1

<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:** [November 3, 2023, 11:35pm UTC](https://discourse.slicer.org/t/projecting-points-to-the-positive-external-surface/32593/1 "2023-11-03T23:35:17Z")

</div>

Dear all,

I am trying to register and deform a surgical plate (roughly pre-registered using a few discrete landmarks) to adapt to the surface of the orbit. The plate has a thickness. You can see that some parts of the pre-registered unbended plate is “sinking” into the orbit.

 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/a/f/afefe00c7938968d9c3d9f4c9726d6a06101b59f.jpeg)

The approach I am testing to adapt the plate to the orbital surface is to 1) sample same points at both the upper and undersurface of the plate, and 2) project the points at the upper surface of the plate to the orbit, and 3) run a registration between the points at the under plate surface to the points projected to the orbit, followed by a thin-plate spline (TPS) transformation (deform the plate at the same type).Though not perfect, it did adapt most of the plate to the surface of the orbit.

Points at the upper surface of the plate. Same points were also sampled at the under surface.

 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/f/5/f550fe527a9896e09926056a1bd438a694bc5ccf.jpeg)

Points projected to the orbital surface from points plated at the upper surface of the plate.

 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/5/8/58d7521c5a56a75689a57d7706dd71d25358a19d.jpeg)

Deformed plate adapt to the orbital surface based on register the points at the undersurface of the plate to the points projected to the orbital surface followed by a thin-plate spline transformation.

 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/7/f/7f0513c75cc1c9c58af9a45e6fe1e7eebb2451e3.jpeg)

However, a small part of the plate was still under the surface. This is because a few points (31, 32, 44) at that region were projected to another side of the orbit due to the presence of a convex area at the medial orbital wall. Thus, a portion of the plate “sank” too deep under that convex area and got too close to under side of the orbital surface.

 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/d/d/dd5b54443f771ddf3befafcb3e499772380cd368.jpeg)  
 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/7/7/77c375577530e6cc03c672653acfb759badd09fa.jpeg)

 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/a/b/ab738b1a7bbb6f8f52c20cad2fae27052d596442.png)

Is there any way to project points into a designated upper surface of the orbit?

The way I do the projection is by using the the `runPointProjection()` function based on the `projectPointsPolydata` in the `ALPACA` module of the `SlicerMorph` extension, which is about casting a ray along the normal of each point and find the intersection with the surface of another model ([https://github.com/SlicerMorph/SlicerMorph/blob/8c1419d0a6da86f8009980f8925a238cbd2fa88d/ALPACA/ALPACA.py#L2977C4-L3073C1](https://github.com/SlicerMorph/SlicerMorph/blob/8c1419d0a6da86f8009980f8925a238cbd2fa88d/ALPACA/ALPACA.py#L2977C4-L3073C1)).

The script I did was:

```auto
import ALPACA
logic = ALPACA.ALPACALogic()
plate_model_node = slicer.util.getNode("preregistered_plate")
lm_plate_upper_node = slicer.util.getNode("lm_plate_upper_surface")
orbit_model_node = slicer.util.getNode("orbit")

projectionFactor = 0.001 #I've been playing with the parameter.
lm_projected_to_orbit = logic.runPointProjection(plate_model_node, orbit_model_node, lm_plate_upper_node , projectionFactor) 
projected_plate_below_to_orbit_lm.SetName("lm_projected_to_orbit")

```

Orbit and plate and landmark files can be accessed at: [orbit\_and\_plate.zip - Google Drive](https://drive.google.com/file/d/1bwM1fcNCERFoZEvuz4DNXbNs_te8FYOR/view?usp=sharing)

Thank you!

Chi

---

<div class="post-metadata">

**Author:** ![pieper](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/pieper/32/8_2.png) [@pieper](https://discourse.slicer.org/u/pieper)\
**Post date:** [November 5, 2023, 3:15pm UTC](https://discourse.slicer.org/t/projecting-points-to-the-positive-external-surface/32593/2 "2023-11-05T15:15:46Z")

</div>

Can you just extend the segmentation at the back of the orbit so that only the front surface is close to the plate?

---

<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:** [November 6, 2023, 1:22am UTC](https://discourse.slicer.org/t/projecting-points-to-the-positive-external-surface/32593/3 "2023-11-06T01:22:06Z")

</div>

Thanks! This sounds like a good idea, at least it would make it much more convenient for data analysis. I’ll give it a try soon. I also need to check with the surgeon if he wants the orbital wall particularly well segmented.

What I did to “lift” the plate out of the orbit was by registering the landmarks at the under surface to those at the upper surface and then transform the undeformed plate accordingly. Iterate the process for a few time would “lift” the plate up.

 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/7/c/7cd57cee3795eed2a1b454f2ca50cf62b92a6a60.jpeg)

I was then able to project all the points from the plate to the upper orbital surface and run a TPS deformation to adapt the plane to the surface of the orbit (visually satisfied at least).

 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/0/a/0a212ee9c99077e6669a1f61eae366b546aff5cb.jpeg)

For repeatability, I am thinking about using something similar to Model-to-Model distance to calculate a scalar value at the plate (e.g., [Python code model-to-model distance - #2 by smrolfe](https://discourse.slicer.org/t/python-code-model-to-model-distance/15593/2)). If the scalar values become all negative, it means the plate has “lifted” to just a position just above the orbit. If this sounds reasonable, is there a way to calculate signed distance between particular landmarks and a surface?

---

<div class="post-metadata">

**Author:** ![mau\_igna\_06](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/mau_igna_06/32/9056_2.png) [@mau\_igna\_06](https://discourse.slicer.org/u/mau_igna_06)\
**Post date:** [November 6, 2023, 6:25pm UTC](https://discourse.slicer.org/t/projecting-points-to-the-positive-external-surface/32593/4 "2023-11-06T18:25:35Z")

</div>

> [@chz31](#):
>
> is there a way to calculate signed distance between particular landmarks and a surface?

The example below should allow you to create a vertex-only polydata you could embed in a vtkMRMLModelNode with [SetAndObservePolyData](https://apidocs.slicer.org/master/classvtkMRMLModelNode.html#a195cf29c5e86b8251fc3fffcc7131827) and then use Model-to-Model distance.

```auto
def __init__ (self, pointlist=[]):
        points = vtk.vtkPoints()
        cellArr = vtk.vtkCellArray()
        Colors = vtk.vtkUnsignedCharArray()
        Colors.SetNumberOfComponents(3)
        Colors.SetName("Colors")
        
        n=0
        for p in pointlist:
            vert = vtk.vtkVertex()
            points.InsertNextPoint(p.x, p.y, p.z)
            vert.GetPointIds().SetId(0,n)
            cellArr.InsertNextCell( vert )
            col = clColor(p.cc())
            Colors.InsertNextTuple3( float(255)*col[0], float(255)*col[1], float(255)*col[2] )
            n=n+1
            
        polydata= vtk.vtkPolyData()
        polydata.SetPoints(points)
        polydata.SetVerts( cellArr )
        polydata.GetPointData().SetScalars(Colors)

```

But if you need spheres instead of vertices (because Model-to-Model module needs surface normals) you could use:

- [VTK: vtkSphereSource Class Reference](https://vtk.org/doc/nightly/html/classvtkSphereSource.html) + [VTK: vtkGlyph3D Class Reference](https://vtk.org/doc/nightly/html/classvtkGlyph3D.html#details)
- or, [VTK: vtkSphereSource Class Reference](https://vtk.org/doc/nightly/html/classvtkSphereSource.html) + [VTK: vtkAppendPolyData Class Reference](https://vtk.org/doc/nightly/html/classvtkAppendPolyData.html)

Hope it helps

---

<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:** [November 8, 2023, 2:42pm UTC](https://discourse.slicer.org/t/projecting-points-to-the-positive-external-surface/32593/5 "2023-11-08T14:42:52Z")

</div>

This looks great. Thank you very much!

---

<div class="post-metadata">

**Author:** ![lassoan](https://sea2.discourse-cdn.com/flex002/user_avatar/discourse.slicer.org/lassoan/32/13_2.png) [@lassoan](https://discourse.slicer.org/u/lassoan)\
**Post date:** [November 8, 2023, 3:17pm UTC](https://discourse.slicer.org/t/projecting-points-to-the-positive-external-surface/32593/6 "2023-11-08T15:17:52Z")

</div>

Several people reported that they could reliable extract a continuous inner surface of the orbital wall using Wrap Solidify effect in Segment Editor (provided by SurfaceWrapSolidify extension). Since it is simple, a single surface, you can snap your plate model directly to it, without any further processing, iterations, etc.

You need to set these inputs to the solidify effect:

 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/9/6/96762423bf40f6f23164cc048f55959000d1920e.jpeg)

You then get the complete, solid model of the orbital cavity in a couple of seconds. Even if the orbital wall is incomplete (which is very often the case, because it is a thin bone and the partial volume effect makes the bone density too similar to soft tissues), the extracted surface is complete and smooth:

 ![image](https://us1.discourse-cdn.com/flex002/uploads/slicer/original/3X/6/d/6d41deb897275529b50da5867b21a6cd631b06f1.jpeg)

---

<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:** [November 9, 2023, 1:25am UTC](https://discourse.slicer.org/t/projecting-points-to-the-positive-external-surface/32593/7 "2023-11-09T01:25:13Z")

</div>

Thanks a lot! I am able to generate an “endocast” of the orbital cavity by using paint brush to create a smaller segment within it and then use the Wrap Solidify to fill the cavity.

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

This tool definitely has lots of potential. It turns out that during surgery, they’ll just remove all the bones surrounding the fracture site and fit the plate to the area. The only hard requirement appears to be aligning the peripheral and the posterior stop (usually the palatine process deep in the orbit). The “endocast”, at least as far as I can tell, captured the peripheral very well, therefore it should be very helpful to use as a reference for adapting the plate. I’ll keep you updated once I get some feedback.
