diff --git a/notebooks/process_surface_morphometrics_output.ipynb b/notebooks/process_surface_morphometrics_output.ipynb
index c278491d055763fe35844b3c05e1ba2297773bde..bae62dd759a401f1378822ac998f78e353c7520e 100644
--- a/notebooks/process_surface_morphometrics_output.ipynb
+++ b/notebooks/process_surface_morphometrics_output.ipynb
@@ -24,7 +24,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 1,
+   "execution_count": null,
    "metadata": {},
    "outputs": [],
    "source": [
@@ -32,6 +32,7 @@
     "import numpy as np\n",
     "from pathlib import Path\n",
     "import pyvista as pv\n",
+    "import pandas as pd\n",
     "\n",
     "import vtk\n",
     "from vtk.util.numpy_support import vtk_to_numpy"
@@ -41,26 +42,7 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "text/plain": [
-       "[PosixPath('/Volumes/ImageAnalysisHub/malbert/lswistak/Segmentation/morphometrics_substacks_smoother_maestro_output/053B41G2_TS_13_bin2_tiltcor_rec_corrected_T3SS_T3SSid_01_MemBrain_seg_v9b.ckpt_segmented_cor_VM.AVV_rh15.vtp'),\n",
-       " PosixPath('/Volumes/ImageAnalysisHub/malbert/lswistak/Segmentation/morphometrics_substacks_smoother_maestro_output/053B40G2_TS_18_bin3_tiltcor_T3SS_T3SSid_00_MemBrain_seg_v9b.ckpt_segmented_cor_VM.AVV_rh15.vtp'),\n",
-       " PosixPath('/Volumes/ImageAnalysisHub/malbert/lswistak/Segmentation/morphometrics_substacks_smoother_maestro_output/060B36G3_TS_12_bin2_tiltcor_rec_corrected_T3SS_T3SSid_02_MemBrain_seg_v9b.ckpt_segmented_cor_VM.AVV_rh15.vtp'),\n",
-       " PosixPath('/Volumes/ImageAnalysisHub/malbert/lswistak/Segmentation/morphometrics_substacks_smoother_maestro_output/053B40G2_TS_18_bin3_tiltcor_T3SS_T3SSid_02_MemBrain_seg_v9b.ckpt_segmented_cor_VM.AVV_rh15.vtp'),\n",
-       " PosixPath('/Volumes/ImageAnalysisHub/malbert/lswistak/Segmentation/morphometrics_substacks_smoother_maestro_output/053B41G2_TS_13_bin2_tiltcor_rec_corrected_T3SS_T3SSid_00_MemBrain_seg_v9b.ckpt_segmented_cor_VM.AVV_rh15.vtp'),\n",
-       " PosixPath('/Volumes/ImageAnalysisHub/malbert/lswistak/Segmentation/morphometrics_substacks_smoother_maestro_output/060B36G3_TS_12_bin2_tiltcor_rec_corrected_T3SS_T3SSid_03_MemBrain_seg_v9b.ckpt_segmented_cor_VM.AVV_rh15.vtp'),\n",
-       " PosixPath('/Volumes/ImageAnalysisHub/malbert/lswistak/Segmentation/morphometrics_substacks_smoother_maestro_output/060B36G3_TS_12_bin2_tiltcor_rec_corrected_T3SS_T3SSid_01_MemBrain_seg_v9b.ckpt_segmented_cor_VM.AVV_rh15.vtp'),\n",
-       " PosixPath('/Volumes/ImageAnalysisHub/malbert/lswistak/Segmentation/morphometrics_substacks_smoother_maestro_output/053B41G2_TS_13_bin2_tiltcor_rec_corrected_T3SS_T3SSid_02_MemBrain_seg_v9b.ckpt_segmented_cor_VM.AVV_rh15.vtp'),\n",
-       " PosixPath('/Volumes/ImageAnalysisHub/malbert/lswistak/Segmentation/morphometrics_substacks_smoother_maestro_output/053B40G2_TS_18_bin3_tiltcor_T3SS_T3SSid_01_MemBrain_seg_v9b.ckpt_segmented_cor_VM.AVV_rh15.vtp')]"
-      ]
-     },
-     "execution_count": 2,
-     "metadata": {},
-     "output_type": "execute_result"
-    }
-   ],
+   "outputs": [],
    "source": [
     "# indicate where data is stored\n",
     "data_dir = Path(\"/Volumes/ImageAnalysisHub/malbert/lswistak/Segmentation/morphometrics_substacks_smoother_maestro_output\")\n",
@@ -68,7 +50,7 @@
     "# output directory\n",
     "out_dir = Path(\"/Volumes/ImageAnalysisHub/malbert/lswistak/Segmentation/morphometrics_substacks_smoother_maestro_output\")\n",
     "\n",
-    "fn_meshes = [f for f in data_dir.iterdir() if f.name.endswith('VM.AVV_rh15.vtp')]\n",
+    "fn_meshes = sorted([f for f in data_dir.iterdir() if f.name.endswith('VM.AVV_rh15.vtp')])\n",
     "fn_meshes = fn_meshes[:]\n",
     "fn_meshes"
    ]
@@ -84,23 +66,7 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
-   "outputs": [
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "Clipping a Dataset by a Bounding Box: 100%|██████████[00:00<00:00]\n",
-      "Clipping a Dataset by a Bounding Box: 100%|██████████[00:00<00:00]\n",
-      "Clipping a Dataset by a Bounding Box: 100%|██████████[00:00<00:00]\n",
-      "Clipping a Dataset by a Bounding Box: 100%|██████████[00:00<00:00]\n",
-      "Clipping a Dataset by a Bounding Box: 100%|██████████[00:00<00:00]\n",
-      "Clipping a Dataset by a Bounding Box: 100%|██████████[00:00<00:00]\n",
-      "Clipping a Dataset by a Bounding Box: 100%|██████████[00:00<00:00]\n",
-      "Clipping a Dataset by a Bounding Box: 100%|██████████[00:00<00:00]\n",
-      "Clipping a Dataset by a Bounding Box: 100%|██████████[00:00<00:00]\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
     "def clip_mesh(m, cut_dist=20):\n",
     "    \"\"\"\n",
@@ -140,16 +106,7 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "Percentiles 5, 95:  [0.0013415629742667078, 0.014907217910513278]\n",
-      "Chosen clim:  [0, 0.02]\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
     "# Determine contrast limits\n",
     "\n",
@@ -176,341 +133,10 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {},
-   "outputs": [
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "df38533f2d4941e5ab134389bf282cc5",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "Widget(value='<iframe src=\"http://localhost:53843/index.html?ui=P_0x1682babf0_0&reconnect=auto\" class=\"pyvista…"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "d850361766454a4995f34ba1894546a8",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "Widget(value='<iframe src=\"http://localhost:53843/index.html?ui=P_0x1770b79a0_1&reconnect=auto\" class=\"pyvista…"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "dd62f8bff2374ada921942eb6b835ea9",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "Widget(value='<iframe src=\"http://localhost:53843/index.html?ui=P_0x177fd97e0_2&reconnect=auto\" class=\"pyvista…"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "9d3b146169674afdacc6bf5e5dbe85e5",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "Widget(value='<iframe src=\"http://localhost:53843/index.html?ui=P_0x1770b59c0_3&reconnect=auto\" class=\"pyvista…"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "e69629cc894349d7ab57b5758d91a4f9",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "Widget(value='<iframe src=\"http://localhost:53843/index.html?ui=P_0x177fdb6a0_4&reconnect=auto\" class=\"pyvista…"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "41b290fa029b4859835ffe3046f0b461",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "Widget(value='<iframe src=\"http://localhost:53843/index.html?ui=P_0x2c272b370_5&reconnect=auto\" class=\"pyvista…"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "e808f222db0242978715603936668ed4",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "Widget(value='<iframe src=\"http://localhost:53843/index.html?ui=P_0x2f9148fa0_6&reconnect=auto\" class=\"pyvista…"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "2f90d496f1a446139a5bc9fccb09a2d2",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "Widget(value='<iframe src=\"http://localhost:53843/index.html?ui=P_0x2c272bbe0_7&reconnect=auto\" class=\"pyvista…"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "data": {
-      "application/vnd.jupyter.widget-view+json": {
-       "model_id": "6c4ed939307649e2abdc309a8d669b18",
-       "version_major": 2,
-       "version_minor": 0
-      },
-      "text/plain": [
-       "Widget(value='<iframe src=\"http://localhost:53843/index.html?ui=P_0x301790fd0_8&reconnect=auto\" class=\"pyvista…"
-      ]
-     },
-     "metadata": {},
-     "output_type": "display_data"
-    },
-    {
-     "name": "stderr",
-     "output_type": "stream",
-     "text": [
-      "Exception raised\n",
-      "KeyError('3cc4844ecb855bd613f859a79bd5f499_367680f')\n",
-      "Traceback (most recent call last):\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/wslink/protocol.py\", line 308, in onCompleteMessage\n",
-      "    results = func(*args, **kwargs)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/protocols/local_rendering.py\", line 33, in get_array\n",
-      "    self.context.get_cached_data_array(data_hash, binary)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/serializers/synchronization_context.py\", line 35, in get_cached_data_array\n",
-      "    cache_obj = self.data_array_cache[p_md5]\n",
-      "KeyError: '3cc4844ecb855bd613f859a79bd5f499_367680f'\n",
-      "\n",
-      "Exception raised\n",
-      "KeyError('afe5a7120a7b58f11f2e1310e14c8456_965684L')\n",
-      "Traceback (most recent call last):\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/wslink/protocol.py\", line 308, in onCompleteMessage\n",
-      "    results = func(*args, **kwargs)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/protocols/local_rendering.py\", line 33, in get_array\n",
-      "    self.context.get_cached_data_array(data_hash, binary)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/serializers/synchronization_context.py\", line 35, in get_cached_data_array\n",
-      "    cache_obj = self.data_array_cache[p_md5]\n",
-      "KeyError: 'afe5a7120a7b58f11f2e1310e14c8456_965684L'\n",
-      "\n",
-      "Exception raised\n",
-      "KeyError('219d31ec359f80d1e10b1b37e50a6b6a_241421f')\n",
-      "Traceback (most recent call last):\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/wslink/protocol.py\", line 308, in onCompleteMessage\n",
-      "    results = func(*args, **kwargs)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/protocols/local_rendering.py\", line 33, in get_array\n",
-      "    self.context.get_cached_data_array(data_hash, binary)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/serializers/synchronization_context.py\", line 35, in get_cached_data_array\n",
-      "    cache_obj = self.data_array_cache[p_md5]\n",
-      "KeyError: '219d31ec359f80d1e10b1b37e50a6b6a_241421f'\n",
-      "\n",
-      "Exception raised\n",
-      "KeyError('6891a32145cad258c63c28f453c51c7f_294012f')\n",
-      "Traceback (most recent call last):\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/wslink/protocol.py\", line 308, in onCompleteMessage\n",
-      "    results = func(*args, **kwargs)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/protocols/local_rendering.py\", line 33, in get_array\n",
-      "    self.context.get_cached_data_array(data_hash, binary)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/serializers/synchronization_context.py\", line 35, in get_cached_data_array\n",
-      "    cache_obj = self.data_array_cache[p_md5]\n",
-      "KeyError: '6891a32145cad258c63c28f453c51c7f_294012f'\n",
-      "\n",
-      "Exception raised\n",
-      "KeyError('5108eca46f5b5924a256bf872c004555_285573f')\n",
-      "Traceback (most recent call last):\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/wslink/protocol.py\", line 308, in onCompleteMessage\n",
-      "    results = func(*args, **kwargs)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/protocols/local_rendering.py\", line 33, in get_array\n",
-      "    self.context.get_cached_data_array(data_hash, binary)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/serializers/synchronization_context.py\", line 35, in get_cached_data_array\n",
-      "    cache_obj = self.data_array_cache[p_md5]\n",
-      "KeyError: '5108eca46f5b5924a256bf872c004555_285573f'\n",
-      "\n",
-      "Exception raised\n",
-      "KeyError('bcf1c32661681338539ebe8d62b427ff_393699f')\n",
-      "Traceback (most recent call last):\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/wslink/protocol.py\", line 308, in onCompleteMessage\n",
-      "    results = func(*args, **kwargs)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/protocols/local_rendering.py\", line 33, in get_array\n",
-      "    self.context.get_cached_data_array(data_hash, binary)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/serializers/synchronization_context.py\", line 35, in get_cached_data_array\n",
-      "    cache_obj = self.data_array_cache[p_md5]\n",
-      "KeyError: 'bcf1c32661681338539ebe8d62b427ff_393699f'\n",
-      "\n",
-      "Exception raised\n",
-      "KeyError('bb2c8b0c162b2d328577843d26088411_770744L')\n",
-      "Traceback (most recent call last):\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/wslink/protocol.py\", line 308, in onCompleteMessage\n",
-      "    results = func(*args, **kwargs)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/protocols/local_rendering.py\", line 33, in get_array\n",
-      "    self.context.get_cached_data_array(data_hash, binary)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/serializers/synchronization_context.py\", line 35, in get_cached_data_array\n",
-      "    cache_obj = self.data_array_cache[p_md5]\n",
-      "KeyError: 'bb2c8b0c162b2d328577843d26088411_770744L'\n",
-      "\n",
-      "Exception raised\n",
-      "KeyError('8a63279b0072f145e970f84f68187c00_367857f')\n",
-      "Traceback (most recent call last):\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/wslink/protocol.py\", line 308, in onCompleteMessage\n",
-      "    results = func(*args, **kwargs)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/protocols/local_rendering.py\", line 33, in get_array\n",
-      "    self.context.get_cached_data_array(data_hash, binary)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/serializers/synchronization_context.py\", line 35, in get_cached_data_array\n",
-      "    cache_obj = self.data_array_cache[p_md5]\n",
-      "KeyError: '8a63279b0072f145e970f84f68187c00_367857f'\n",
-      "\n",
-      "Exception raised\n",
-      "KeyError('214d53af2441df75be96540bf4d8b2c0_748124L')\n",
-      "Traceback (most recent call last):\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/wslink/protocol.py\", line 308, in onCompleteMessage\n",
-      "    results = func(*args, **kwargs)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/protocols/local_rendering.py\", line 33, in get_array\n",
-      "    self.context.get_cached_data_array(data_hash, binary)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/serializers/synchronization_context.py\", line 35, in get_cached_data_array\n",
-      "    cache_obj = self.data_array_cache[p_md5]\n",
-      "KeyError: '214d53af2441df75be96540bf4d8b2c0_748124L'\n",
-      "\n",
-      "Exception raised\n",
-      "KeyError('dd6a6937fd3a91de9722306045411a0a_1035696L')\n",
-      "Traceback (most recent call last):\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/wslink/protocol.py\", line 308, in onCompleteMessage\n",
-      "    results = func(*args, **kwargs)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/protocols/local_rendering.py\", line 33, in get_array\n",
-      "    self.context.get_cached_data_array(data_hash, binary)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/serializers/synchronization_context.py\", line 35, in get_cached_data_array\n",
-      "    cache_obj = self.data_array_cache[p_md5]\n",
-      "KeyError: 'dd6a6937fd3a91de9722306045411a0a_1035696L'\n",
-      "\n",
-      "Exception raised\n",
-      "KeyError('9c0bf85e28acd36474fb776da8ccf9ac_375120f')\n",
-      "Traceback (most recent call last):\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/wslink/protocol.py\", line 308, in onCompleteMessage\n",
-      "    results = func(*args, **kwargs)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/protocols/local_rendering.py\", line 33, in get_array\n",
-      "    self.context.get_cached_data_array(data_hash, binary)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/serializers/synchronization_context.py\", line 35, in get_cached_data_array\n",
-      "    cache_obj = self.data_array_cache[p_md5]\n",
-      "KeyError: '9c0bf85e28acd36474fb776da8ccf9ac_375120f'\n",
-      "\n",
-      "Exception raised\n",
-      "KeyError('019fefd144a152bd97be6d09debcc4f2_192686f')\n",
-      "Traceback (most recent call last):\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/wslink/protocol.py\", line 308, in onCompleteMessage\n",
-      "    results = func(*args, **kwargs)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/protocols/local_rendering.py\", line 33, in get_array\n",
-      "    self.context.get_cached_data_array(data_hash, binary)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/serializers/synchronization_context.py\", line 35, in get_cached_data_array\n",
-      "    cache_obj = self.data_array_cache[p_md5]\n",
-      "KeyError: '019fefd144a152bd97be6d09debcc4f2_192686f'\n",
-      "\n",
-      "Exception raised\n",
-      "KeyError('9a2f12c008a478445a0a4f5988344fc2_961604L')\n",
-      "Traceback (most recent call last):\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/wslink/protocol.py\", line 308, in onCompleteMessage\n",
-      "    results = func(*args, **kwargs)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/protocols/local_rendering.py\", line 33, in get_array\n",
-      "    self.context.get_cached_data_array(data_hash, binary)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/serializers/synchronization_context.py\", line 35, in get_cached_data_array\n",
-      "    cache_obj = self.data_array_cache[p_md5]\n",
-      "KeyError: '9a2f12c008a478445a0a4f5988344fc2_961604L'\n",
-      "\n",
-      "Exception raised\n",
-      "KeyError('95f9497724465d747225ff227ea1156a_187031f')\n",
-      "Traceback (most recent call last):\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/wslink/protocol.py\", line 308, in onCompleteMessage\n",
-      "    results = func(*args, **kwargs)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/protocols/local_rendering.py\", line 33, in get_array\n",
-      "    self.context.get_cached_data_array(data_hash, binary)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/serializers/synchronization_context.py\", line 35, in get_cached_data_array\n",
-      "    cache_obj = self.data_array_cache[p_md5]\n",
-      "KeyError: '95f9497724465d747225ff227ea1156a_187031f'\n",
-      "\n",
-      "Exception raised\n",
-      "KeyError('2ffbedca03f54bb4a66c0eba146dccaa_258924f')\n",
-      "Traceback (most recent call last):\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/wslink/protocol.py\", line 308, in onCompleteMessage\n",
-      "    results = func(*args, **kwargs)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/protocols/local_rendering.py\", line 33, in get_array\n",
-      "    self.context.get_cached_data_array(data_hash, binary)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/serializers/synchronization_context.py\", line 35, in get_cached_data_array\n",
-      "    cache_obj = self.data_array_cache[p_md5]\n",
-      "KeyError: '2ffbedca03f54bb4a66c0eba146dccaa_258924f'\n",
-      "\n",
-      "Exception raised\n",
-      "KeyError('ba5e15d1344dae8d64d54e97e6ea457a_984696L')\n",
-      "Traceback (most recent call last):\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/wslink/protocol.py\", line 308, in onCompleteMessage\n",
-      "    results = func(*args, **kwargs)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/protocols/local_rendering.py\", line 33, in get_array\n",
-      "    self.context.get_cached_data_array(data_hash, binary)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/serializers/synchronization_context.py\", line 35, in get_cached_data_array\n",
-      "    cache_obj = self.data_array_cache[p_md5]\n",
-      "KeyError: 'ba5e15d1344dae8d64d54e97e6ea457a_984696L'\n",
-      "\n",
-      "Exception raised\n",
-      "KeyError('33a4d4d8cc81fe8abcef095d79b02f49_240401f')\n",
-      "Traceback (most recent call last):\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/wslink/protocol.py\", line 308, in onCompleteMessage\n",
-      "    results = func(*args, **kwargs)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/protocols/local_rendering.py\", line 33, in get_array\n",
-      "    self.context.get_cached_data_array(data_hash, binary)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/serializers/synchronization_context.py\", line 35, in get_cached_data_array\n",
-      "    cache_obj = self.data_array_cache[p_md5]\n",
-      "KeyError: '33a4d4d8cc81fe8abcef095d79b02f49_240401f'\n",
-      "\n",
-      "Exception raised\n",
-      "KeyError('e7d77123fcb0ab156297119cae8f4564_246174f')\n",
-      "Traceback (most recent call last):\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/wslink/protocol.py\", line 308, in onCompleteMessage\n",
-      "    results = func(*args, **kwargs)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/protocols/local_rendering.py\", line 33, in get_array\n",
-      "    self.context.get_cached_data_array(data_hash, binary)\n",
-      "  File \"/Users/malbert/mambaforge/envs/pyvista/lib/python3.10/site-packages/trame_vtk/modules/vtk/serializers/synchronization_context.py\", line 35, in get_cached_data_array\n",
-      "    cache_obj = self.data_array_cache[p_md5]\n",
-      "KeyError: 'e7d77123fcb0ab156297119cae8f4564_246174f'\n",
-      "\n"
-     ]
-    }
-   ],
+   "outputs": [],
    "source": [
     "for i, (fn, polydata) in enumerate(zip(fn_meshes, polydatas)):\n",
-    "    # if i > 2:\n",
+    "    # if i > 0:\n",
     "    #     break\n",
     "\n",
     "    pl = pv.Plotter()\n",
@@ -520,6 +146,7 @@
     "        color='Grey',\n",
     "        scalars='curvedness_VV',\n",
     "        clim=clim,\n",
+    "        cmap='coolwarm',\n",
     "        )\n",
     "\n",
     "    # add IM and OM meshes\n",
@@ -541,11 +168,374 @@
     "\n",
     "    pl.export_html(os.path.join(out_dir, fn.stem + '.curvedness.html'))\n"
    ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Quantification"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "## Read in coordinates\n",
+    "\n",
+    "import pickle\n",
+    "df = pickle.load(open(os.path.join(\"/Volumes/Eirene/Points/extracted_images\", 'T3SS_coordinates.pc'), 'rb'))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# extract x,y,z from df dataframe where contour_id == 2 and source_fn without .mod is in substack_paths\n",
+    "\n",
+    "sdfs = []\n",
+    "for p in fn_meshes:\n",
+    "    tmpdf = df[(df['source_fn'].str.replace('.mod', '') == p.name.split('_T3SSid')[0])]\n",
+    "    # (df['contour_id'] == 2)\n",
+    "    tmpdf = tmpdf[tmpdf['object_id'] == int(p.name.split('_MemBrain_seg')[0][-2:])]\n",
+    "    tmpdf['substack_path'] = p\n",
+    "    sdfs.append(tmpdf)\n",
+    "sdf = pd.concat(sdfs)\n",
+    "sdf = sdf.reset_index()\n",
+    "sdf"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# alphabetical order\n",
+    "ruptured = [\n",
+    "    0,\n",
+    "    0,\n",
+    "    0,\n",
+    "    1,\n",
+    "    0,\n",
+    "    0,\n",
+    "    0,\n",
+    "    1,\n",
+    "    1,\n",
+    "]\n",
+    "\n",
+    "curvdfs = []\n",
+    "poss = []\n",
+    "for ind, p in enumerate(fn_meshes[:]):\n",
+    "\n",
+    "    m = polydatas[ind]\n",
+    "\n",
+    "    c = vtk_to_numpy(m.GetCellData().GetArray('curvedness_VV'))\n",
+    "\n",
+    "    # C_px = 150 * 10 / float(sdf.iloc[ind]['voxel_size'])\n",
+    "    C_phys = 150\n",
+    "\n",
+    "    pos = float(sdf.iloc[ind]['voxel_size']) / 10. * np.array([sdf.iloc[ind]['%s' %dim]['12_t'] for dim in ['z', 'y', 'x']]) + C_phys\n",
+    "    pos0 = float(sdf.iloc[ind]['voxel_size']) / 10. * np.array([sdf.iloc[ind]['%s' %dim]['10_t'] for dim in ['z', 'y', 'x']]) + C_phys\n",
+    "    pos1 = np.array([C_phys] * 3)\n",
+    "\n",
+    "    poss.append([pos0, pos1, pos])\n",
+    "\n",
+    "    pts = vtk_to_numpy(m.GetCellData().GetArray('xyz'))\n",
+    "\n",
+    "    # calc distance to pos\n",
+    "    dist = np.linalg.norm(pts - pos, axis=1)\n",
+    "\n",
+    "    print('Distance to pos: ', np.min(dist))\n",
+    "\n",
+    "    curvdf = dict()\n",
+    "    curvdf['curvedness'] = c\n",
+    "    curvdf['distance'] = dist\n",
+    "    curvdf['index'] = np.arange(len(c))\n",
+    "    curvdf['file'] = p.name\n",
+    "    curvdf['touching'] = 'touching' if np.min(dist) < 10 else 'not touching'\n",
+    "    curvdf['ruptured'] = ruptured[ind]\n",
+    "    curvdf['condition'] = 'ruptured' if ruptured[ind] else ('touching' if np.min(dist) < 10 else 'not touching')\n",
+    "    curvdf = pd.DataFrame(curvdf)\n",
+    "    curvdfs.append(curvdf)\n",
+    "\n",
+    "curvdf = pd.concat(curvdfs)\n",
+    "\n",
+    "# count number of files per condition\n",
+    "curvdf.groupby(['touching', 'file']).count()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import seaborn as sns\n",
+    "from matplotlib import pyplot as plt\n",
+    "\n",
+    "# bin distances into bins of width 20\n",
+    "curvdf['distance_bin'] = pd.cut(curvdf['distance'], bins=np.arange(0, 100, 10))\n",
+    "\n",
+    "plt.figure()\n",
+    "tmp = curvdf.dropna().groupby([\n",
+    "    'distance_bin',\n",
+    "    'file',\n",
+    "    ]).agg({'curvedness': 'mean', 'touching': 'first'})\n",
+    "\n",
+    "palette ={\"touching\": \"gray\", \"not touching\": \"lightgray\"}\n",
+    "\n",
+    "g = sns.barplot(data=tmp, x='distance_bin', y='curvedness',#, errorbar=('pi'),\n",
+    "    palette=palette,\n",
+    "    # color=\"gray\",\n",
+    "    hue='touching',\n",
+    "    )\n",
+    "\n",
+    "for patch in g.patches:\n",
+    "    clr = patch.get_facecolor()\n",
+    "    patch.set_edgecolor('black')\n",
+    "\n",
+    "plt.title(\"Curvedness vs distance\")\n",
+    "plt.xlabel('Distance to T3SS tip [nm]')\n",
+    "plt.ylabel('Curvedness')\n",
+    "plt.savefig(os.path.join(out_dir, 'curvedness_vs_distance.pdf'))\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import seaborn as sns\n",
+    "from matplotlib import pyplot as plt\n",
+    "\n",
+    "# bin distances into bins of width 20\n",
+    "curvdf['distance_bin'] = pd.cut(curvdf['distance'], bins=np.arange(0, 100, 10))\n",
+    "\n",
+    "plt.figure()\n",
+    "tmp = curvdf.dropna().groupby([\n",
+    "    'distance_bin',\n",
+    "    'file',\n",
+    "    ]).agg({'curvedness': 'mean', 'touching': 'first'})\n",
+    "\n",
+    "# keep only touching\n",
+    "tmp = tmp[tmp['touching'] == 'touching']\n",
+    "\n",
+    "palette ={\"touching\": \"gray\", \"not touching\": \"white\"}\n",
+    "\n",
+    "g = sns.barplot(data=tmp, x='distance_bin', y='curvedness',# errorbar=('pi'),\n",
+    "    # palette=palette,\n",
+    "    color=\"lightgray\",\n",
+    "    )\n",
+    "\n",
+    "for patch in g.patches:\n",
+    "    clr = patch.get_facecolor()\n",
+    "    patch.set_edgecolor('black')\n",
+    "\n",
+    "plt.title(\"Curvedness vs distance - only touching\")\n",
+    "plt.xlabel('Distance to T3SS tip [nm]')\n",
+    "plt.ylabel('Curvedness')\n",
+    "plt.savefig(os.path.join(out_dir, 'curvedness_vs_distance_only_touching.pdf'))\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import seaborn as sns\n",
+    "from matplotlib import pyplot as plt\n",
+    "\n",
+    "# bin distances into bins of width 20\n",
+    "curvdf['distance_bin'] = pd.cut(curvdf['distance'], bins=np.arange(0, 100, 10))\n",
+    "\n",
+    "plt.figure()\n",
+    "tmp = curvdf.dropna().groupby([\n",
+    "    'distance_bin',\n",
+    "    'file',\n",
+    "    ]).agg({'curvedness': 'mean', 'touching': 'first'}).reset_index()\n",
+    "\n",
+    "# keep only touching\n",
+    "tmp = tmp[tmp['touching'] == 'touching']\n",
+    "tmp['distance_bin_center'] = tmp['distance_bin'].apply(lambda x: x.mid)\n",
+    "\n",
+    "palette ={\"touching\": \"gray\", \"not touching\": \"white\"}\n",
+    "\n",
+    "g = sns.lineplot(data=tmp, x='distance_bin_center', y='curvedness', errorbar=('sd'),\n",
+    "    # palette=palette,\n",
+    "    color=\"gray\",\n",
+    "    )\n",
+    "\n",
+    "for patch in g.patches:\n",
+    "    clr = patch.get_facecolor()\n",
+    "    patch.set_edgecolor('black')\n",
+    "\n",
+    "plt.title(\"Curvedness vs distance - only touching\")\n",
+    "plt.xlabel('Distance to T3SS tip [nm]')\n",
+    "plt.ylabel('Curvedness')\n",
+    "plt.savefig(os.path.join(out_dir, 'curvedness_vs_distance_only_touching_line.pdf'))\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import seaborn as sns\n",
+    "from matplotlib import pyplot as plt\n",
+    "\n",
+    "# bin distances into bins of width 20\n",
+    "curvdf['distance_bin'] = pd.cut(curvdf['distance'], bins=np.arange(0, 100, 10))\n",
+    "\n",
+    "plt.figure()\n",
+    "tmp = curvdf.dropna().groupby([\n",
+    "    'distance_bin',\n",
+    "    'file',\n",
+    "    ]).agg({'curvedness': 'mean', 'touching': 'first'})\n",
+    "\n",
+    "palette ={\"touching\": \"gray\", \"not touching\": \"white\"}\n",
+    "\n",
+    "g = sns.barplot(data=tmp, x='distance_bin', y='curvedness',#, errorbar=('pi'),\n",
+    "    # palette=palette,\n",
+    "    color=\"lightgray\",\n",
+    "    )\n",
+    "\n",
+    "for patch in g.patches:\n",
+    "    clr = patch.get_facecolor()\n",
+    "    patch.set_edgecolor('black')\n",
+    "\n",
+    "plt.title(\"Curvedness vs distance - all substacks\")\n",
+    "plt.xlabel('Distance to T3SS tip [nm]')\n",
+    "plt.ylabel('Curvedness')\n",
+    "plt.savefig(os.path.join(out_dir, 'curvedness_vs_distance_all.pdf'))\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import seaborn as sns\n",
+    "from matplotlib import pyplot as plt\n",
+    "\n",
+    "# bin distances into bins of width 20\n",
+    "curvdf['distance_bin'] = pd.cut(curvdf['distance'], bins=np.arange(0, 100, 10))\n",
+    "\n",
+    "# tmp = curvdf.dropna().groupby([\n",
+    "#     'distance_bin',\n",
+    "#     'file',\n",
+    "#     ]).agg({'curvedness': 'mean', 'touching': 'first'})\n",
+    "tmp = curvdf\n",
+    "\n",
+    "tmp = tmp.reset_index().dropna()\n",
+    "tmp['distance_centers'] = [i.mid for i in tmp['distance_bin']]\n",
+    "tmp['substack'] = [i.split('_Mem')[0] for i in tmp['file']]\n",
+    "\n",
+    "# palette ={\"touching\": \"gray\", \"not touching\": \"white\"}\n",
+    "\n",
+    "plt.figure(figsize=(15, 8))\n",
+    "# plt.figure()\n",
+    "# plt.figure(figsize=(10, 5))\n",
+    "g = sns.barplot(data=tmp,\n",
+    "    # x='distance_centers',\n",
+    "    x='distance_bin',\n",
+    "    y='curvedness', errorbar=('pi', 50),\n",
+    "    # palette=palette,\n",
+    "    # color=\"lightgray\",\n",
+    "    hue='substack',\n",
+    "    )"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Distance visualization"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "for i, (fn, polydata) in enumerate(zip(fn_meshes, polydatas)):\n",
+    "    # if i != 0:\n",
+    "    #     continue\n",
+    "\n",
+    "    pl = pv.Plotter()\n",
+    "\n",
+    "    tmp = curvdf[curvdf.file == fn.name]\n",
+    "    # dist %10 rounds to 0\n",
+    "    tmp['boundary'] = (tmp['distance'] % 10) <= 1.\n",
+    "\n",
+    "    s = np.array(tmp['curvedness'])\n",
+    "    s[tmp['boundary']] = np.nan\n",
+    "\n",
+    "    pl.add_mesh(\n",
+    "        polydata,\n",
+    "        color='Grey',\n",
+    "        scalars=s,\n",
+    "        clim=clim,\n",
+    "        nan_color='white',\n",
+    "        cmap='coolwarm',\n",
+    "        opacity=1.,\n",
+    "        )\n",
+    "\n",
+    "    # add IM and OM meshes\n",
+    "\n",
+    "    for label in [\"IM\", \"OM\"]:\n",
+    "        # replace VM in fn with IM or OM\n",
+    "        fn_label = fn.stem.replace(\"VM\", label)\n",
+    "        fn_label = fn_label + fn.suffix\n",
+    "        # read the mesh\n",
+    "        polydata_label = read_vtp(data_dir / fn_label)\n",
+    "        \n",
+    "        pl.add_mesh(\n",
+    "            polydata_label,\n",
+    "            color='Grey',\n",
+    "            opacity=0.5,\n",
+    "        )\n",
+    "\n",
+    "    pl.add_lines(np.array(\n",
+    "        [poss[i][0], poss[i][1], poss[i][1], poss[i][2]]),\n",
+    "        color='black',\n",
+    "        width=6,\n",
+    "        )\n",
+    "\n",
+    "    # pl.add_points(\n",
+    "    #     np.array([poss[i][0], poss[i][1], poss[i][2]]), color='black',\n",
+    "    #         point_size=10, style=\"points\",\n",
+    "    #         render_points_as_spheres=True,\n",
+    "    # )\n",
+    "\n",
+    "    for j in range(3):\n",
+    "        pl.add_mesh(\n",
+    "            pv.SolidSphere(\n",
+    "                center=poss[i][j],\n",
+    "                outer_radius=3,\n",
+    "                # color='black',\n",
+    "                ),\n",
+    "            color='black',\n",
+    "            )\n",
+    "\n",
+    "    pl.show()\n",
+    "\n",
+    "    pl.export_html(os.path.join(out_dir, fn.stem + '.curvedness_distances.html'))\n",
+    "    print(os.path.join(out_dir, fn.stem + '.curvedness_distances.html'))\n"
+   ]
   }
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python 3",
+   "display_name": "t3ss_pyvista",
    "language": "python",
    "name": "python3"
   },
@@ -559,7 +549,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.10.14"
+   "version": "3.11.11"
   }
  },
  "nbformat": 4,
diff --git a/notebooks/process_surface_morphometrics_output_curv.ipynb b/notebooks/process_surface_morphometrics_output_curv.ipynb
deleted file mode 100644
index bae62dd759a401f1378822ac998f78e353c7520e..0000000000000000000000000000000000000000
--- a/notebooks/process_surface_morphometrics_output_curv.ipynb
+++ /dev/null
@@ -1,557 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "# Membrane curvature visualization\n",
-    "\n",
-    "This notebook is used to visualize membrane curvature as calculated by `pycurv` and processed using https://github.com/GrotjahnLab/surface_morphometrics.\n",
-    "\n"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## Software environment\n",
-    "Use this notebook with a conda env:\n",
-    "\n",
-    "- `conda create -n t3ss_rec python=3.10`\n",
-    "- `conda activate t3ss_rec`\n",
-    "- `pip install jupyter matplotlib ipympl pyvista`"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import os\n",
-    "import numpy as np\n",
-    "from pathlib import Path\n",
-    "import pyvista as pv\n",
-    "import pandas as pd\n",
-    "\n",
-    "import vtk\n",
-    "from vtk.util.numpy_support import vtk_to_numpy"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "# indicate where data is stored\n",
-    "data_dir = Path(\"/Volumes/ImageAnalysisHub/malbert/lswistak/Segmentation/morphometrics_substacks_smoother_maestro_output\")\n",
-    "\n",
-    "# output directory\n",
-    "out_dir = Path(\"/Volumes/ImageAnalysisHub/malbert/lswistak/Segmentation/morphometrics_substacks_smoother_maestro_output\")\n",
-    "\n",
-    "fn_meshes = sorted([f for f in data_dir.iterdir() if f.name.endswith('VM.AVV_rh15.vtp')])\n",
-    "fn_meshes = fn_meshes[:]\n",
-    "fn_meshes"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## Load data"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "def clip_mesh(m, cut_dist=20):\n",
-    "    \"\"\"\n",
-    "    Clip meshes to avoid border artifacts\n",
-    "    \"\"\"\n",
-    "    # bounds = [float(func(m.points[:, dim], 0))\n",
-    "    # for dim in range(3) for func in [np.min, np.max]]\n",
-    "    # bounds[0] = cut_dist\n",
-    "    # bounds[1] = 300 - cut_dist\n",
-    "    # bounds[2] = cut_dist\n",
-    "    # bounds[3] = 300 - cut_dist\n",
-    "    # bounds[4] = cut_dist\n",
-    "    # bounds[5] = 300 - cut_dist\n",
-    "    bounds = [cut_dist, 300 - cut_dist] * 3\n",
-    "    mc = m.clip_box(bounds, progress_bar=True, invert=False)\n",
-    "    return mc\n",
-    "\n",
-    "def read_vtp(fn):\n",
-    "    \"\"\"\n",
-    "    Read the pycurv output files in vtp format into pyvista meshes\n",
-    "    \"\"\"\n",
-    "\n",
-    "    reader = vtk.vtkXMLPolyDataReader()\n",
-    "    reader.SetFileName(fn)\n",
-    "    reader.Update()\n",
-    "    polyDataOutput = reader.GetOutput()\n",
-    "    polyDataOutput\n",
-    "\n",
-    "    polydata = reader.GetOutput()\n",
-    "\n",
-    "    return pv.PolyData(polydata)\n",
-    "\n",
-    "polydatas = [clip_mesh(read_vtp(fn)) for fn in fn_meshes]"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "# Determine contrast limits\n",
-    "\n",
-    "scalarss = [vtk_to_numpy(polydata.GetCellData().GetArray('curvedness_VV'))\n",
-    "    for polydata in polydatas]\n",
-    "\n",
-    "all_scalars = np.concatenate(scalarss)\n",
-    "clim = [np.percentile(all_scalars, 5), np.percentile(all_scalars, 95)]\n",
-    "\n",
-    "print('Percentiles 5, 95: ', clim)\n",
-    "\n",
-    "clim = [0, 0.02]\n",
-    "print('Chosen clim: ', clim)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## Visualize"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "for i, (fn, polydata) in enumerate(zip(fn_meshes, polydatas)):\n",
-    "    # if i > 0:\n",
-    "    #     break\n",
-    "\n",
-    "    pl = pv.Plotter()\n",
-    "\n",
-    "    pl.add_mesh(\n",
-    "        polydata,\n",
-    "        color='Grey',\n",
-    "        scalars='curvedness_VV',\n",
-    "        clim=clim,\n",
-    "        cmap='coolwarm',\n",
-    "        )\n",
-    "\n",
-    "    # add IM and OM meshes\n",
-    "\n",
-    "    for label in [\"IM\", \"OM\"]:\n",
-    "        # replace VM in fn with IM or OM\n",
-    "        fn_label = fn.stem.replace(\"VM\", label)\n",
-    "        fn_label = fn_label + fn.suffix\n",
-    "        # read the mesh\n",
-    "        polydata_label = read_vtp(data_dir / fn_label)\n",
-    "        \n",
-    "        pl.add_mesh(\n",
-    "            polydata_label,\n",
-    "            color='Grey',\n",
-    "            opacity=0.5,\n",
-    "        )\n",
-    "\n",
-    "    pl.show()\n",
-    "\n",
-    "    pl.export_html(os.path.join(out_dir, fn.stem + '.curvedness.html'))\n"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## Quantification"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "## Read in coordinates\n",
-    "\n",
-    "import pickle\n",
-    "df = pickle.load(open(os.path.join(\"/Volumes/Eirene/Points/extracted_images\", 'T3SS_coordinates.pc'), 'rb'))"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "# extract x,y,z from df dataframe where contour_id == 2 and source_fn without .mod is in substack_paths\n",
-    "\n",
-    "sdfs = []\n",
-    "for p in fn_meshes:\n",
-    "    tmpdf = df[(df['source_fn'].str.replace('.mod', '') == p.name.split('_T3SSid')[0])]\n",
-    "    # (df['contour_id'] == 2)\n",
-    "    tmpdf = tmpdf[tmpdf['object_id'] == int(p.name.split('_MemBrain_seg')[0][-2:])]\n",
-    "    tmpdf['substack_path'] = p\n",
-    "    sdfs.append(tmpdf)\n",
-    "sdf = pd.concat(sdfs)\n",
-    "sdf = sdf.reset_index()\n",
-    "sdf"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "# alphabetical order\n",
-    "ruptured = [\n",
-    "    0,\n",
-    "    0,\n",
-    "    0,\n",
-    "    1,\n",
-    "    0,\n",
-    "    0,\n",
-    "    0,\n",
-    "    1,\n",
-    "    1,\n",
-    "]\n",
-    "\n",
-    "curvdfs = []\n",
-    "poss = []\n",
-    "for ind, p in enumerate(fn_meshes[:]):\n",
-    "\n",
-    "    m = polydatas[ind]\n",
-    "\n",
-    "    c = vtk_to_numpy(m.GetCellData().GetArray('curvedness_VV'))\n",
-    "\n",
-    "    # C_px = 150 * 10 / float(sdf.iloc[ind]['voxel_size'])\n",
-    "    C_phys = 150\n",
-    "\n",
-    "    pos = float(sdf.iloc[ind]['voxel_size']) / 10. * np.array([sdf.iloc[ind]['%s' %dim]['12_t'] for dim in ['z', 'y', 'x']]) + C_phys\n",
-    "    pos0 = float(sdf.iloc[ind]['voxel_size']) / 10. * np.array([sdf.iloc[ind]['%s' %dim]['10_t'] for dim in ['z', 'y', 'x']]) + C_phys\n",
-    "    pos1 = np.array([C_phys] * 3)\n",
-    "\n",
-    "    poss.append([pos0, pos1, pos])\n",
-    "\n",
-    "    pts = vtk_to_numpy(m.GetCellData().GetArray('xyz'))\n",
-    "\n",
-    "    # calc distance to pos\n",
-    "    dist = np.linalg.norm(pts - pos, axis=1)\n",
-    "\n",
-    "    print('Distance to pos: ', np.min(dist))\n",
-    "\n",
-    "    curvdf = dict()\n",
-    "    curvdf['curvedness'] = c\n",
-    "    curvdf['distance'] = dist\n",
-    "    curvdf['index'] = np.arange(len(c))\n",
-    "    curvdf['file'] = p.name\n",
-    "    curvdf['touching'] = 'touching' if np.min(dist) < 10 else 'not touching'\n",
-    "    curvdf['ruptured'] = ruptured[ind]\n",
-    "    curvdf['condition'] = 'ruptured' if ruptured[ind] else ('touching' if np.min(dist) < 10 else 'not touching')\n",
-    "    curvdf = pd.DataFrame(curvdf)\n",
-    "    curvdfs.append(curvdf)\n",
-    "\n",
-    "curvdf = pd.concat(curvdfs)\n",
-    "\n",
-    "# count number of files per condition\n",
-    "curvdf.groupby(['touching', 'file']).count()"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import seaborn as sns\n",
-    "from matplotlib import pyplot as plt\n",
-    "\n",
-    "# bin distances into bins of width 20\n",
-    "curvdf['distance_bin'] = pd.cut(curvdf['distance'], bins=np.arange(0, 100, 10))\n",
-    "\n",
-    "plt.figure()\n",
-    "tmp = curvdf.dropna().groupby([\n",
-    "    'distance_bin',\n",
-    "    'file',\n",
-    "    ]).agg({'curvedness': 'mean', 'touching': 'first'})\n",
-    "\n",
-    "palette ={\"touching\": \"gray\", \"not touching\": \"lightgray\"}\n",
-    "\n",
-    "g = sns.barplot(data=tmp, x='distance_bin', y='curvedness',#, errorbar=('pi'),\n",
-    "    palette=palette,\n",
-    "    # color=\"gray\",\n",
-    "    hue='touching',\n",
-    "    )\n",
-    "\n",
-    "for patch in g.patches:\n",
-    "    clr = patch.get_facecolor()\n",
-    "    patch.set_edgecolor('black')\n",
-    "\n",
-    "plt.title(\"Curvedness vs distance\")\n",
-    "plt.xlabel('Distance to T3SS tip [nm]')\n",
-    "plt.ylabel('Curvedness')\n",
-    "plt.savefig(os.path.join(out_dir, 'curvedness_vs_distance.pdf'))\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import seaborn as sns\n",
-    "from matplotlib import pyplot as plt\n",
-    "\n",
-    "# bin distances into bins of width 20\n",
-    "curvdf['distance_bin'] = pd.cut(curvdf['distance'], bins=np.arange(0, 100, 10))\n",
-    "\n",
-    "plt.figure()\n",
-    "tmp = curvdf.dropna().groupby([\n",
-    "    'distance_bin',\n",
-    "    'file',\n",
-    "    ]).agg({'curvedness': 'mean', 'touching': 'first'})\n",
-    "\n",
-    "# keep only touching\n",
-    "tmp = tmp[tmp['touching'] == 'touching']\n",
-    "\n",
-    "palette ={\"touching\": \"gray\", \"not touching\": \"white\"}\n",
-    "\n",
-    "g = sns.barplot(data=tmp, x='distance_bin', y='curvedness',# errorbar=('pi'),\n",
-    "    # palette=palette,\n",
-    "    color=\"lightgray\",\n",
-    "    )\n",
-    "\n",
-    "for patch in g.patches:\n",
-    "    clr = patch.get_facecolor()\n",
-    "    patch.set_edgecolor('black')\n",
-    "\n",
-    "plt.title(\"Curvedness vs distance - only touching\")\n",
-    "plt.xlabel('Distance to T3SS tip [nm]')\n",
-    "plt.ylabel('Curvedness')\n",
-    "plt.savefig(os.path.join(out_dir, 'curvedness_vs_distance_only_touching.pdf'))\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import seaborn as sns\n",
-    "from matplotlib import pyplot as plt\n",
-    "\n",
-    "# bin distances into bins of width 20\n",
-    "curvdf['distance_bin'] = pd.cut(curvdf['distance'], bins=np.arange(0, 100, 10))\n",
-    "\n",
-    "plt.figure()\n",
-    "tmp = curvdf.dropna().groupby([\n",
-    "    'distance_bin',\n",
-    "    'file',\n",
-    "    ]).agg({'curvedness': 'mean', 'touching': 'first'}).reset_index()\n",
-    "\n",
-    "# keep only touching\n",
-    "tmp = tmp[tmp['touching'] == 'touching']\n",
-    "tmp['distance_bin_center'] = tmp['distance_bin'].apply(lambda x: x.mid)\n",
-    "\n",
-    "palette ={\"touching\": \"gray\", \"not touching\": \"white\"}\n",
-    "\n",
-    "g = sns.lineplot(data=tmp, x='distance_bin_center', y='curvedness', errorbar=('sd'),\n",
-    "    # palette=palette,\n",
-    "    color=\"gray\",\n",
-    "    )\n",
-    "\n",
-    "for patch in g.patches:\n",
-    "    clr = patch.get_facecolor()\n",
-    "    patch.set_edgecolor('black')\n",
-    "\n",
-    "plt.title(\"Curvedness vs distance - only touching\")\n",
-    "plt.xlabel('Distance to T3SS tip [nm]')\n",
-    "plt.ylabel('Curvedness')\n",
-    "plt.savefig(os.path.join(out_dir, 'curvedness_vs_distance_only_touching_line.pdf'))\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import seaborn as sns\n",
-    "from matplotlib import pyplot as plt\n",
-    "\n",
-    "# bin distances into bins of width 20\n",
-    "curvdf['distance_bin'] = pd.cut(curvdf['distance'], bins=np.arange(0, 100, 10))\n",
-    "\n",
-    "plt.figure()\n",
-    "tmp = curvdf.dropna().groupby([\n",
-    "    'distance_bin',\n",
-    "    'file',\n",
-    "    ]).agg({'curvedness': 'mean', 'touching': 'first'})\n",
-    "\n",
-    "palette ={\"touching\": \"gray\", \"not touching\": \"white\"}\n",
-    "\n",
-    "g = sns.barplot(data=tmp, x='distance_bin', y='curvedness',#, errorbar=('pi'),\n",
-    "    # palette=palette,\n",
-    "    color=\"lightgray\",\n",
-    "    )\n",
-    "\n",
-    "for patch in g.patches:\n",
-    "    clr = patch.get_facecolor()\n",
-    "    patch.set_edgecolor('black')\n",
-    "\n",
-    "plt.title(\"Curvedness vs distance - all substacks\")\n",
-    "plt.xlabel('Distance to T3SS tip [nm]')\n",
-    "plt.ylabel('Curvedness')\n",
-    "plt.savefig(os.path.join(out_dir, 'curvedness_vs_distance_all.pdf'))\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "import seaborn as sns\n",
-    "from matplotlib import pyplot as plt\n",
-    "\n",
-    "# bin distances into bins of width 20\n",
-    "curvdf['distance_bin'] = pd.cut(curvdf['distance'], bins=np.arange(0, 100, 10))\n",
-    "\n",
-    "# tmp = curvdf.dropna().groupby([\n",
-    "#     'distance_bin',\n",
-    "#     'file',\n",
-    "#     ]).agg({'curvedness': 'mean', 'touching': 'first'})\n",
-    "tmp = curvdf\n",
-    "\n",
-    "tmp = tmp.reset_index().dropna()\n",
-    "tmp['distance_centers'] = [i.mid for i in tmp['distance_bin']]\n",
-    "tmp['substack'] = [i.split('_Mem')[0] for i in tmp['file']]\n",
-    "\n",
-    "# palette ={\"touching\": \"gray\", \"not touching\": \"white\"}\n",
-    "\n",
-    "plt.figure(figsize=(15, 8))\n",
-    "# plt.figure()\n",
-    "# plt.figure(figsize=(10, 5))\n",
-    "g = sns.barplot(data=tmp,\n",
-    "    # x='distance_centers',\n",
-    "    x='distance_bin',\n",
-    "    y='curvedness', errorbar=('pi', 50),\n",
-    "    # palette=palette,\n",
-    "    # color=\"lightgray\",\n",
-    "    hue='substack',\n",
-    "    )"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## Distance visualization"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "for i, (fn, polydata) in enumerate(zip(fn_meshes, polydatas)):\n",
-    "    # if i != 0:\n",
-    "    #     continue\n",
-    "\n",
-    "    pl = pv.Plotter()\n",
-    "\n",
-    "    tmp = curvdf[curvdf.file == fn.name]\n",
-    "    # dist %10 rounds to 0\n",
-    "    tmp['boundary'] = (tmp['distance'] % 10) <= 1.\n",
-    "\n",
-    "    s = np.array(tmp['curvedness'])\n",
-    "    s[tmp['boundary']] = np.nan\n",
-    "\n",
-    "    pl.add_mesh(\n",
-    "        polydata,\n",
-    "        color='Grey',\n",
-    "        scalars=s,\n",
-    "        clim=clim,\n",
-    "        nan_color='white',\n",
-    "        cmap='coolwarm',\n",
-    "        opacity=1.,\n",
-    "        )\n",
-    "\n",
-    "    # add IM and OM meshes\n",
-    "\n",
-    "    for label in [\"IM\", \"OM\"]:\n",
-    "        # replace VM in fn with IM or OM\n",
-    "        fn_label = fn.stem.replace(\"VM\", label)\n",
-    "        fn_label = fn_label + fn.suffix\n",
-    "        # read the mesh\n",
-    "        polydata_label = read_vtp(data_dir / fn_label)\n",
-    "        \n",
-    "        pl.add_mesh(\n",
-    "            polydata_label,\n",
-    "            color='Grey',\n",
-    "            opacity=0.5,\n",
-    "        )\n",
-    "\n",
-    "    pl.add_lines(np.array(\n",
-    "        [poss[i][0], poss[i][1], poss[i][1], poss[i][2]]),\n",
-    "        color='black',\n",
-    "        width=6,\n",
-    "        )\n",
-    "\n",
-    "    # pl.add_points(\n",
-    "    #     np.array([poss[i][0], poss[i][1], poss[i][2]]), color='black',\n",
-    "    #         point_size=10, style=\"points\",\n",
-    "    #         render_points_as_spheres=True,\n",
-    "    # )\n",
-    "\n",
-    "    for j in range(3):\n",
-    "        pl.add_mesh(\n",
-    "            pv.SolidSphere(\n",
-    "                center=poss[i][j],\n",
-    "                outer_radius=3,\n",
-    "                # color='black',\n",
-    "                ),\n",
-    "            color='black',\n",
-    "            )\n",
-    "\n",
-    "    pl.show()\n",
-    "\n",
-    "    pl.export_html(os.path.join(out_dir, fn.stem + '.curvedness_distances.html'))\n",
-    "    print(os.path.join(out_dir, fn.stem + '.curvedness_distances.html'))\n"
-   ]
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "t3ss_pyvista",
-   "language": "python",
-   "name": "python3"
-  },
-  "language_info": {
-   "codemirror_mode": {
-    "name": "ipython",
-    "version": 3
-   },
-   "file_extension": ".py",
-   "mimetype": "text/x-python",
-   "name": "python",
-   "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython3",
-   "version": "3.11.11"
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}