multimodars 0.2.2__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. {multimodars-0.2.2 → multimodars-0.3.0}/.github/ISSUE_TEMPLATE/bug_report.md +8 -15
  2. {multimodars-0.2.2 → multimodars-0.3.0}/.github/PULL_REQUEST_TEMPLATE.md +1 -1
  3. {multimodars-0.2.2 → multimodars-0.3.0}/.github/workflows/CI.yml +22 -1
  4. multimodars-0.3.0/.pre-commit-config.yaml +52 -0
  5. multimodars-0.3.0/CHANGELOG.md +138 -0
  6. multimodars-0.3.0/CONTRIBUTING.md +164 -0
  7. {multimodars-0.2.2 → multimodars-0.3.0}/Cargo.lock +1 -27
  8. {multimodars-0.2.2 → multimodars-0.3.0}/Cargo.toml +1 -2
  9. {multimodars-0.2.2 → multimodars-0.3.0}/PKG-INFO +15 -9
  10. {multimodars-0.2.2 → multimodars-0.3.0}/README.md +7 -6
  11. multimodars-0.3.0/ROADMAP.MD +90 -0
  12. multimodars-0.3.0/docs/api/adjust_ccta.rst +9 -0
  13. {multimodars-0.2.2 → multimodars-0.3.0}/docs/api/centerline.rst +2 -1
  14. {multimodars-0.2.2 → multimodars-0.3.0}/docs/api/classes/PyContour.rst +1 -1
  15. {multimodars-0.2.2 → multimodars-0.3.0}/docs/api/classes/PyGeometryPair.rst +1 -1
  16. multimodars-0.3.0/docs/api/converters_and_wrappers.rst +9 -0
  17. {multimodars-0.2.2 → multimodars-0.3.0}/docs/api/index.rst +2 -1
  18. {multimodars-0.2.2 → multimodars-0.3.0}/docs/conf.py +4 -10
  19. multimodars-0.3.0/docs/figures/dataprep.jpg +0 -0
  20. multimodars-0.3.0/docs/index.rst +82 -0
  21. {multimodars-0.2.2 → multimodars-0.3.0}/docs/installation.rst +1 -1
  22. multimodars-0.3.0/docs/tutorial_ccta.rst +485 -0
  23. multimodars-0.3.0/docs/tutorial_intravascular.rst +621 -0
  24. multimodars-0.3.0/multimodars/__init__.py +133 -0
  25. {multimodars-0.2.2 → multimodars-0.3.0}/multimodars/_converters.py +222 -91
  26. multimodars-0.3.0/multimodars/_processing.py +1426 -0
  27. multimodars-0.3.0/multimodars/ccta/__init__.py +466 -0
  28. multimodars-0.3.0/multimodars/ccta/debug_plots.py +207 -0
  29. multimodars-0.3.0/multimodars/ccta/fixing_functions.py +239 -0
  30. multimodars-0.3.0/multimodars/ccta/labeling.py +561 -0
  31. multimodars-0.3.0/multimodars/ccta/manipulating.py +977 -0
  32. {multimodars-0.2.2 → multimodars-0.3.0}/multimodars/io/write_geometries.py +3 -2
  33. multimodars-0.3.0/multimodars/multimodars.pyi +707 -0
  34. multimodars-0.3.0/multimodars/py.typed +0 -0
  35. multimodars-0.3.0/paper/REVIEWER1.md +140 -0
  36. {multimodars-0.2.2 → multimodars-0.3.0}/paper/bibliography.bib +6 -3
  37. multimodars-0.3.0/paper/figures/Figure2.jpg +0 -0
  38. {multimodars-0.2.2 → multimodars-0.3.0}/paper/paper.md +3 -3
  39. {multimodars-0.2.2 → multimodars-0.3.0}/pyproject.toml +10 -3
  40. {multimodars-0.2.2 → multimodars-0.3.0}/src/ccta/adjust_mesh/label_coronary.rs +1 -3
  41. {multimodars-0.2.2 → multimodars-0.3.0}/src/ccta/adjust_mesh/scale_coronary.rs +97 -42
  42. multimodars-0.3.0/src/ccta/binding/ccta_py.rs +461 -0
  43. {multimodars-0.2.2 → multimodars-0.3.0}/src/ccta/mod.rs +0 -1
  44. multimodars-0.3.0/src/intravascular/binding/align.rs +335 -0
  45. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/binding/classes.rs +601 -271
  46. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/binding/entry.rs +186 -195
  47. multimodars-0.3.0/src/intravascular/binding/mod.rs +1512 -0
  48. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/centerline_align/align_algorithms.rs +1 -1
  49. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/centerline_align/mod.rs +13 -13
  50. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/io/geometry.rs +91 -26
  51. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/io/input.rs +10 -13
  52. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/io/integrity_check.rs +18 -21
  53. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/io/mod.rs +2 -5
  54. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/io/output.rs +1 -1
  55. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/processing/align_between.rs +16 -11
  56. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/processing/align_within.rs +18 -20
  57. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/processing/postprocessing.rs +3 -9
  58. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/processing/preprocessing.rs +55 -22
  59. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/processing/process_utils.rs +2 -4
  60. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/processing/wall.rs +11 -19
  61. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/to_object/interpolation.rs +8 -8
  62. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/to_object/mod.rs +2 -0
  63. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/to_object/texture.rs +1 -1
  64. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/to_object/write_mtl.rs +5 -5
  65. multimodars-0.2.2/src/intravascular/utils/utils.rs → multimodars-0.3.0/src/intravascular/utils/general_utils.rs +4 -4
  66. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/utils/mod.rs +1 -1
  67. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/utils/test_utils.rs +11 -11
  68. {multimodars-0.2.2 → multimodars-0.3.0}/src/lib.rs +5 -2
  69. multimodars-0.3.0/tests/test_ccta.py +657 -0
  70. {multimodars-0.2.2 → multimodars-0.3.0}/tests/test_wrappers.py +3 -5
  71. multimodars-0.2.2/.pre-commit-config.yaml +0 -38
  72. multimodars-0.2.2/CHANGELOG.md +0 -68
  73. multimodars-0.2.2/CONTRIBUTING.md +0 -61
  74. multimodars-0.2.2/ROADMAP.MD +0 -63
  75. multimodars-0.2.2/docs/api/converters_and_wrappers.rst +0 -13
  76. multimodars-0.2.2/docs/index.rst +0 -69
  77. multimodars-0.2.2/docs/tutorial_ccta.rst +0 -158
  78. multimodars-0.2.2/docs/tutorial_intravascular.rst +0 -411
  79. multimodars-0.2.2/multimodars/__init__.py +0 -63
  80. multimodars-0.2.2/multimodars/_wrappers.py +0 -725
  81. multimodars-0.2.2/multimodars/ccta/__init__.py +0 -1
  82. multimodars-0.2.2/multimodars/ccta/adjust_ccta.py +0 -689
  83. multimodars-0.2.2/paper/figures/Figure2.jpg +0 -0
  84. multimodars-0.2.2/src/ccta/binding/ccta_py.rs +0 -305
  85. multimodars-0.2.2/src/ccta/io/input.rs +0 -220
  86. multimodars-0.2.2/src/ccta/io/mod.rs +0 -1
  87. multimodars-0.2.2/src/intravascular/binding/align.rs +0 -267
  88. multimodars-0.2.2/src/intravascular/binding/mod.rs +0 -1274
  89. {multimodars-0.2.2 → multimodars-0.3.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  90. {multimodars-0.2.2 → multimodars-0.3.0}/.github/workflows/draft-pdf.yml +0 -0
  91. {multimodars-0.2.2 → multimodars-0.3.0}/.gitignore +0 -0
  92. {multimodars-0.2.2 → multimodars-0.3.0}/.readthedocs.yaml +0 -0
  93. {multimodars-0.2.2 → multimodars-0.3.0}/LICENSE +0 -0
  94. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/idealized_geometry/branch_diastolic_contours.csv +0 -0
  95. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/idealized_geometry/branch_systolic_contours.csv +0 -0
  96. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/idealized_geometry/calcium_diastolic_contours.csv +0 -0
  97. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/idealized_geometry/calcium_systolic_contours.csv +0 -0
  98. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/idealized_geometry/diastolic_contours.csv +0 -0
  99. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/idealized_geometry/diastolic_reference_points.csv +0 -0
  100. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/idealized_geometry/eem_diastolic_contours.csv +0 -0
  101. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/idealized_geometry/eem_systolic_contours.csv +0 -0
  102. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/idealized_geometry/systolic_contours.csv +0 -0
  103. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/idealized_geometry/systolic_reference_points.csv +0 -0
  104. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/ivus_full/branch_diastolic_contours.csv +0 -0
  105. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/ivus_full/branch_systolic_contours.csv +0 -0
  106. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/ivus_full/calcium_diastolic_contours.csv +0 -0
  107. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/ivus_full/calcium_systolic_contours.csv +0 -0
  108. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/ivus_full/diastolic_contours.csv +0 -0
  109. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/ivus_full/diastolic_reference_points.csv +0 -0
  110. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/ivus_full/eem_diastolic_contours.csv +0 -0
  111. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/ivus_full/eem_systolic_contours.csv +0 -0
  112. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/ivus_full/systolic_contours.csv +0 -0
  113. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/ivus_full/systolic_reference_points.csv +0 -0
  114. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/ivus_rest/combined_sorted_manual.csv +0 -0
  115. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/ivus_rest/diastolic_contours.csv +0 -0
  116. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/ivus_rest/diastolic_reference_points.csv +0 -0
  117. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/ivus_rest/systolic_contours.csv +0 -0
  118. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/ivus_rest/systolic_reference_points.csv +0 -0
  119. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/ivus_stress/combined_sorted_manual.csv +0 -0
  120. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/ivus_stress/diastolic_contours.csv +0 -0
  121. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/ivus_stress/diastolic_reference_points.csv +0 -0
  122. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/ivus_stress/systolic_contours.csv +0 -0
  123. {multimodars-0.2.2 → multimodars-0.3.0}/data/fixtures/ivus_stress/systolic_reference_points.csv +0 -0
  124. {multimodars-0.2.2 → multimodars-0.3.0}/docs/Makefile +0 -0
  125. {multimodars-0.2.2 → multimodars-0.3.0}/docs/_static/.gitkeep +0 -0
  126. {multimodars-0.2.2 → multimodars-0.3.0}/docs/api/classes/PyCenterline.rst +0 -0
  127. {multimodars-0.2.2 → multimodars-0.3.0}/docs/api/classes/PyCenterlinePoint.rst +0 -0
  128. {multimodars-0.2.2 → multimodars-0.3.0}/docs/api/classes/PyContourPoint.rst +0 -0
  129. {multimodars-0.2.2 → multimodars-0.3.0}/docs/api/classes/PyFrame.rst +0 -0
  130. {multimodars-0.2.2 → multimodars-0.3.0}/docs/api/classes/PyGeometry.rst +0 -0
  131. {multimodars-0.2.2 → multimodars-0.3.0}/docs/api/classes/PyInputData.rst +0 -0
  132. {multimodars-0.2.2 → multimodars-0.3.0}/docs/api/classes/PyRecord.rst +0 -0
  133. {multimodars-0.2.2 → multimodars-0.3.0}/docs/api/classes/index.rst +0 -0
  134. {multimodars-0.2.2 → multimodars-0.3.0}/docs/api/core.rst +0 -0
  135. {multimodars-0.2.2 → multimodars-0.3.0}/docs/api/processing.rst +0 -0
  136. {multimodars-0.2.2 → multimodars-0.3.0}/docs/api/utilities.rst +0 -0
  137. {multimodars-0.2.2 → multimodars-0.3.0}/docs/build.sh +0 -0
  138. {multimodars-0.2.2 → multimodars-0.3.0}/docs/changelog.md +0 -0
  139. {multimodars-0.2.2 → multimodars-0.3.0}/docs/figures/Alignment3p.png +0 -0
  140. {multimodars-0.2.2 → multimodars-0.3.0}/docs/figures/animation_stress_induced_systolic_deformation.gif +0 -0
  141. {multimodars-0.2.2 → multimodars-0.3.0}/docs/figures/anomalous_labeling.jpg +0 -0
  142. {multimodars-0.2.2 → multimodars-0.3.0}/docs/figures/initial_labeling.jpg +0 -0
  143. {multimodars-0.2.2 → multimodars-0.3.0}/docs/figures/scaling.jpg +0 -0
  144. {multimodars-0.2.2 → multimodars-0.3.0}/docs/make.bat +0 -0
  145. {multimodars-0.2.2 → multimodars-0.3.0}/media/coronary_artery_disease.jpg +0 -0
  146. {multimodars-0.2.2 → multimodars-0.3.0}/media/dynamic_lumen_changes.png +0 -0
  147. {multimodars-0.2.2 → multimodars-0.3.0}/media/multimoda-rs.jpg +0 -0
  148. {multimodars-0.2.2 → multimodars-0.3.0}/multimodars/io/__init__.py +0 -0
  149. {multimodars-0.2.2 → multimodars-0.3.0}/multimodars/io/read_geometrical.py +0 -0
  150. {multimodars-0.2.2 → multimodars-0.3.0}/paper/figures/Figure1.jpg +0 -0
  151. {multimodars-0.2.2 → multimodars-0.3.0}/paper/figures/Figure3.png +0 -0
  152. {multimodars-0.2.2 → multimodars-0.3.0}/paper/figures/Figure4.jpg +0 -0
  153. {multimodars-0.2.2 → multimodars-0.3.0}/src/ccta/adjust_mesh/mod.rs +0 -0
  154. {multimodars-0.2.2 → multimodars-0.3.0}/src/ccta/binding/mod.rs +0 -0
  155. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/centerline_align/preprocessing.rs +0 -0
  156. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/mod.rs +0 -0
  157. {multimodars-0.2.2 → multimodars-0.3.0}/src/intravascular/processing/mod.rs +0 -0
  158. {multimodars-0.2.2 → multimodars-0.3.0}/tests/conftest.py +0 -0
  159. {multimodars-0.2.2 → multimodars-0.3.0}/tests/test_converters.py +0 -0
  160. {multimodars-0.2.2 → multimodars-0.3.0}/tests/test_core.py +0 -0
  161. {multimodars-0.2.2 → multimodars-0.3.0}/tests/tests_todo.md +0 -0
@@ -12,27 +12,20 @@ A clear and concise description of what the bug is.
12
12
 
13
13
  **To Reproduce**
14
14
  Steps to reproduce the behavior:
15
- 1. Go to '...'
16
- 2. Click on '....'
17
- 3. Scroll down to '....'
18
- 4. See error
15
+ 1. Load data with '...'
16
+ 2. Call function '...'
17
+ 3. See error
19
18
 
20
19
  **Expected behavior**
21
20
  A clear and concise description of what you expected to happen.
22
21
 
22
+ **Environment (please complete the following information):**
23
+ - OS: [e.g. Windows, Linux, macOS]
24
+ - Python version: [e.g. 3.10]
25
+ - Package version: [e.g. 0.1.0]
26
+
23
27
  **Screenshots**
24
28
  If applicable, add screenshots to help explain your problem.
25
29
 
26
- **Desktop (please complete the following information):**
27
- - OS: [e.g. iOS]
28
- - Browser [e.g. chrome, safari]
29
- - Version [e.g. 22]
30
-
31
- **Smartphone (please complete the following information):**
32
- - Device: [e.g. iPhone6]
33
- - OS: [e.g. iOS8.1]
34
- - Browser [e.g. stock browser, safari]
35
- - Version [e.g. 22]
36
-
37
30
  **Additional context**
38
31
  Add any other context about the problem here.
@@ -1,7 +1,7 @@
1
1
  ## Pull Request Checklist
2
2
 
3
3
  - [ ] I have added tests that prove the fix is effective or that the feature works.
4
- - [ ] I ran `pytest` (or `nox` / your CI command) locally and all tests pass.
4
+ - [ ] I ran `pytest` locally and all tests pass.
5
5
  - [ ] I updated `CHANGELOG.md` (or NEWS) and bumped the package version if applicable.
6
6
  - [ ] I added/updated documentation (README, docstrings, or docs).
7
7
  - [ ] This PR is linked to an issue: fixes #<issue-number> (if applicable).
@@ -19,6 +19,27 @@ permissions:
19
19
  contents: read
20
20
 
21
21
  jobs:
22
+ python-compat:
23
+ name: Python ${{ matrix.python-version }} compatibility
24
+ runs-on: ubuntu-latest
25
+ strategy:
26
+ fail-fast: false
27
+ matrix:
28
+ python-version: ['3.10', '3.11', '3.12', '3.13']
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+ - uses: actions/setup-python@v5
32
+ with:
33
+ python-version: ${{ matrix.python-version }}
34
+ - name: Install Rust
35
+ uses: dtolnay/rust-toolchain@stable
36
+ - name: Install Python deps
37
+ run: |
38
+ python -m pip install --upgrade pip
39
+ pip install -e .[test]
40
+ - name: Run Python tests
41
+ run: pytest
42
+
22
43
  linux:
23
44
  runs-on: ${{ matrix.platform.runner }}
24
45
  strategy:
@@ -205,7 +226,7 @@ jobs:
205
226
  name: Release
206
227
  runs-on: ubuntu-latest
207
228
  if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }}
208
- needs: [linux, musllinux, windows, macos, sdist]
229
+ needs: [linux, musllinux, windows, macos, sdist, python-compat]
209
230
  permissions:
210
231
  id-token: write
211
232
  contents: write
@@ -0,0 +1,52 @@
1
+ repos:
2
+ - repo: local
3
+ hooks:
4
+ # - id: pytest
5
+ # name: Run pytest
6
+ # entry: pytest
7
+ # language: system
8
+ # types: [python]
9
+ # pass_filenames: false
10
+
11
+ - id: rust-fmt
12
+ name: Rust format
13
+ entry: cargo fmt --all
14
+ language: system
15
+ types: [rust]
16
+ pass_filenames: false
17
+
18
+ - id: rust-clippy
19
+ name: Rust clippy
20
+ entry: cargo clippy --all-targets --all-features -- -D warnings
21
+ language: system
22
+ types: [rust]
23
+ pass_filenames: false
24
+
25
+ # - id: rust-test
26
+ # name: Rust unit tests
27
+ # entry: cargo test --lib # --quiet
28
+ # language: system
29
+ # types: [rust]
30
+ # pass_filenames: false
31
+
32
+ - id: python-black
33
+ name: Black formatting
34
+ entry: black
35
+ language: system
36
+ types: [python]
37
+ require_serial: true
38
+ args: [--quiet, --target-version, py312]
39
+
40
+ - id: python-ruff
41
+ name: Ruff linting
42
+ entry: ruff check
43
+ language: system
44
+ types: [python]
45
+ args: [--fix]
46
+
47
+ - id: python-mypy
48
+ name: Mypy type checking
49
+ entry: mypy multimodars --ignore-missing-imports
50
+ language: system
51
+ types: [python]
52
+ pass_filenames: false
@@ -0,0 +1,138 @@
1
+ # Changelog multimodars
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ This project adheres to [Semantic Versioning](https://semver.org/).
5
+
6
+ ## [0.3.0] - 2026-04-30
7
+
8
+ ### Added
9
+ - `stitch_ccta_to_intravascular()`: fuses an aligned intravascular `PyGeometry` with a CCTA
10
+ trimesh by triangulating a patch between the open boundary ring of the CCTA mesh and the
11
+ proximal/distal contour of the intravascular geometry. Supports two boundary-ring alignment
12
+ modes (`"nearest_iv"` and `"highest_z"`) for proximal and distal ends independently.
13
+ - `fix_and_remesh_stitched_mesh()`: repairs the stitched surface (non-manifold edges/vertices,
14
+ open holes) and applies isotropic remeshing via `pymeshlab`. Requires the optional
15
+ `multimodars[meshlab]` extra.
16
+ - `manual_hole_fill()`: flat-fills remaining open holes in a trimesh after automatic repair.
17
+ - `remove_labeled_points_from_mesh()`: deletes one or more labeled vertex regions from the mesh, remaps faces, and returns the open boundary ring as `"boundary_points"` for downstream stitching.
18
+ - `keep_labeled_points_from_mesh()`: retains only the vertices of a specified labeled region,
19
+ discarding all others.
20
+ - `sync_results_to_mesh()`: refreshes all coordinate lists in the results dictionary after
21
+ `scale_region_centerline_morphing()` moves vertices, keeping labels consistent with the
22
+ updated mesh.
23
+ - `find_aortic_wall_scaling()`: computes the optimal radial scaling factor for the aortic wall
24
+ region by minimising the distance to the first intravascular frame whose lumen elliptic ratio
25
+ drops below 1.3 (transition from intramural to free-segment lumen).
26
+ - `export_section_stl()`: exports the full mesh or a labeled sub-region (`"all"`, `"aorta"`,
27
+ `"rca"`, `"lca"`) as an STL file.
28
+ - `plot_results_key()`: opens an interactive 3-D trimesh scene visualising selected labeled
29
+ regions with colour coding (yellow = aorta, blue = RCA, green = LCA, red = removed/intramural,
30
+ cyan = proximal, magenta = distal, orange = anomalous).
31
+ - Tests for all new CCTA functions.
32
+ - `mypy` added to pre-commit hooks for static type checking.
33
+
34
+ ### Changed
35
+ - Rust-side CCTA STL reader (`src/ccta/io/input.rs`) removed; mesh I/O is now handled
36
+ entirely via Python `trimesh`, eliminating the `stl_io` dependency.
37
+ - `pymeshlab` and `pyglet` are now optional dependencies; the core package installs without
38
+ them. Install extras with `pip install 'multimodars[meshlab]'`.
39
+ - Debug-plot helpers moved to a dedicated `ccta.debug_plots` module.
40
+ - CCTA Python module refactored into focused sub-modules (`labeling`, `manipulating`,
41
+ `fixing_functions`, `debug_plots`).
42
+ - Parameter order updated in several CCTA API calls for consistency.
43
+ - `diastole` argument removed from functions where it was not used.
44
+ - Labeling of aorta boundary points improved to avoid incorrect assignment at mesh edges.
45
+ - Type stubs updated and extended to cover all new public functions.
46
+ - Clippy lints applied across the Rust codebase (non-breaking).
47
+
48
+ ### Fixed
49
+ - 3-D area and elliptic ratio calculation corrected for non-planar contours.
50
+ - Mesh face normals corrected after stitching to ensure consistent outward orientation.
51
+
52
+ ### Documentation
53
+ - CCTA tutorial completely rewritten to cover the full stitching workflow:
54
+ `remove_labeled_points_from_mesh` → `stitch_ccta_to_intravascular` →
55
+ `fix_and_remesh_stitched_mesh` → Taubin smoothing → `export_section_stl` → re-labeling.
56
+ - Intravascular tutorial reviewed and completed: section numbering corrected (1–7),
57
+ CSV-vs-numpy workflow note added, column descriptions with units added to data tables,
58
+ Sphinx cross-references added throughout, `translate()` tuple-argument bug fixed.
59
+
60
+
61
+ ## [0.2.3] - 2026-03-06
62
+
63
+ ### Added
64
+ - Type stubs for the Rust-implemented interface (via maturin)
65
+ - `PyFrame.get_frame_at_z`: retrieve a frame by its z position
66
+ - `PyFrame.get_frame_at_index`: retrieve a frame by its index
67
+ - `PyFrame.replace_frame`: replace an existing frame with another
68
+
69
+ ### Changed
70
+ - CI pipeline now tests against Python 3.10 through 3.13
71
+ - Updated documentation to reflect the current interface and Python version requirements
72
+ - Added direct download links to example data in the README and documentation
73
+ - Migrated to modern NumPy-style type annotations throughout the Python codebase
74
+ - All imports are now top-level across all Python modules
75
+
76
+ ## [0.2.2] - 2026-03-03
77
+
78
+ ### Changed
79
+ - Uploaded new example data
80
+ - Updated docs to match the new example data
81
+
82
+ ## [0.2.1] - 2026-01-12
83
+
84
+ ### Changed
85
+ - Automatic version update based on Cargo.toml
86
+
87
+ ## [0.2.0] - 2026-01-11
88
+
89
+ ### Added
90
+ - Functionality to read in CCTA .stl files and label them according to centerline object
91
+ - Functionality to label the CCTA geometry additionally by an aligned intravascular aligned geometry.
92
+ - Functionality to better align intravascular geometry to a centerline using additionally the CCTA vertices `align_combined`
93
+ - Functionality to radially scale different regions radially to their corresponding centerline
94
+ - Algorihtm for finding the best radial scaling factor for distal and proximal region compared to the aligned geometry
95
+ - Algorithm for finding the best radial scaling factor for the aorta to minimize the distance between "wall" of `PyGeometry` object and the identified intramural vertices
96
+
97
+ ### Changed
98
+ - Updated documentation to include a tutorial for the new functionality
99
+ - Reduced the amount of testing data to reduce size of the package
100
+ - Removed the examples folder from the repository and coupled to release (.zip file)
101
+
102
+ ## [0.1.2] - 2025-11-21
103
+
104
+ ### Changed
105
+ - intravascular::processing::align_within::fill_holes() now does not panic with gaps > 2
106
+ frames, but instead still fixes the holes and returns a warning to the user to check integrity of the data.
107
+
108
+ ## [0.1.1] - 2025-11-17
109
+
110
+ ### Changed
111
+ - Update documentation to match new functionality
112
+
113
+ ## [0.1.0] - 2025-11-17
114
+
115
+ ### Added
116
+ - Fill-holes functionality: when a geometry has a large gap between frames,
117
+ up to two frames will be inserted to improve continuity.
118
+ - Geometry integrity tests: after aligning frames within a geometry,
119
+ several integrity checks are performed before continuing the pipeline.
120
+ - `InputData`: the entire pipeline can be driven from an `InputData` object
121
+ that includes coordinates for different contours.
122
+ - Enum for contour types: `Lumen`, `EEM`, `sidebranch`, `calcification`,
123
+ `wall`, `catheter`.
124
+ - `PyFrame` struct: each frame can hold multiple contour types which are
125
+ processed in parallel.
126
+
127
+ ### Changed
128
+ - Full architecture refactor:
129
+ - Raw data is read and ordered into a `Geometry` struct.
130
+ - The `Geometry` struct undergoes integrity checks to ensure data quality.
131
+ - Alignment is performed first *within* geometries, then *between* geometries.
132
+ - Modules are loosely coupled to improve maintainability.
133
+ - Parallelization model updated: each geometry is processed in parallel;
134
+ within-geometry alignment runs first, then between-geometry alignment runs in parallel.
135
+ - Multiple bug fixes in the alignment algorithms.
136
+
137
+ ### Fixed
138
+ - Various stability and indexing bugs discovered during alignment testing.
@@ -0,0 +1,164 @@
1
+ # ✩°。⋆ Contributing to the project ⋆。°✩
2
+
3
+ First off, thank you for considering contributing! **ദ്ദി(˵ •̀ ᴗ - ˵ ) ✧**
4
+ Whether you’re filing a bug, proposing a new feature, improving documentation, or refactoring code, your help makes this project better for you and the whole community.
5
+
6
+ ## Code of Conduct
7
+ This project adheres to the [Contributor Covenant v2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).
8
+ By participating, you agree to respect everyone in this community. **˗ˏˋ ♡ ˎˊ˗**
9
+
10
+ ## Table of Contents
11
+
12
+ 1. [Getting Started](#getting-started)
13
+ 2. [I Have a Question](#i-have-a-question)
14
+ 3. [Reporting Bugs](#reporting-bugs)
15
+ 4. [Suggesting Enhancements](#suggesting-enhancements)
16
+ 5. [Your First Code Contribution](#your-first-code-contribution)
17
+ 6. [Pull Request Process](#pull-request-process)
18
+ 7. [Coding Style & Tests](#coding-style--tests)
19
+ 8. [Writing Documentation](#writing-documentation)
20
+ 9. [Where to Get Help](#where-to-get-help)
21
+
22
+ ## Getting started
23
+ 1. Fork the repo and clone your fork
24
+ ```bash
25
+ git clone https://github.com/yungselm/multimoda-rs.git
26
+ cd multimoda-rs
27
+ ```
28
+ 2. Create a new branch
29
+ ```bash
30
+ git checkout -b feature/sick-feature
31
+ ```
32
+ 3. install dependencies and run tests
33
+ ```bash
34
+ pip install -e
35
+ pytest
36
+ ```
37
+ ## I have a Question
38
+ Before opening an issue:
39
+ - Read the [Documentation](https://multimoda-rs.readthedocs.io/en/latest/index.html)
40
+ - Search existing [Issues](https://github.com/yungselm/multimoda-rs/issues?q=is%3Aissue)
41
+
42
+ If you still need help:
43
+ - Open a new issue: [Click here](https://github.com/yungselm/multimoda-rs/issues/new/choose)
44
+ - Provide:
45
+ - A clear, descriptive title
46
+ - Context: what you're trying to do, expected vs. actual behaviour
47
+ - Project verion, OS/platform, Python version, and any relevant logs
48
+
49
+ ## Reporting Bugs
50
+ If you find a bug, please help us fix it by opening a [a new issue](https://github.com/yungselm/multimoda-rs/issues/new/choose) and providing:
51
+ - **Title**: A short descriptive title
52
+ - **Steps to reproduce**: Minimal code snippet or sequence to trigger the bug
53
+ - **Expected behaviour** vs **Actual behaviour**
54
+ - **Environment**:
55
+ - `multimoda-rs` version
56
+ - Python version
57
+ - OS and architecture
58
+
59
+ ## Suggesting Enhancements
60
+ Have an idea for a new feature or an improvement to existing behaviour? Open a [feature request issue](https://github.com/yungselm/multimoda-rs/issues/new/choose) and include:
61
+ - **Summary**: A concise description of the enhancement
62
+ - **Motivation**: Why would this be useful? What problem does it solve?
63
+ - **Proposed solution**: How you envision it working (API sketch, example usage, etc.)
64
+ - **Alternatives considered**: Other approaches you've thought about
65
+
66
+ We appreciate well-scoped proposals with clear use cases.
67
+
68
+ ## Your First Code Contribution
69
+ Not sure where to start? Look for issues labelled [`good first issue`](https://github.com/yungselm/multimoda-rs/issues?q=is%3Aissue+label%3A%22good+first+issue%22) or [`help wanted`](https://github.com/yungselm/multimoda-rs/issues?q=is%3Aissue+label%3A%22help+wanted%22).
70
+
71
+ **Setup** (Python + Rust hybrid via [maturin](https://github.com/PyO3/maturin)):
72
+ ```bash
73
+ # 1. Fork the repo on GitHub, then clone your fork
74
+ git clone https://github.com/<your-username>/multimoda-rs.git
75
+ cd multimoda-rs
76
+
77
+ # 2. Create and activate a virtual environment
78
+ python -m venv .venv
79
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
80
+
81
+ # 3. Install the package in development mode (compiles the Rust extension)
82
+ pip install -e ".[dev]"
83
+
84
+ # 4. Install pre-commit hooks
85
+ pre-commit install
86
+
87
+ # 5. Verify everything works
88
+ pytest
89
+ cargo test --lib
90
+ ```
91
+
92
+ ## Pull Request Process
93
+ 1. **Branch** – create a descriptive branch (`feature/my-feature`, `fix/issue-123`).
94
+ 2. **Commit** – write clear, atomic commit messages.
95
+ 3. **Tests** – add or update tests to cover your changes; all existing tests must pass.
96
+ 4. **Docs** – update docstrings and, if applicable, the RST docs under `docs/`.
97
+ 5. **Open the PR** – target the `main` branch. Fill in the PR template with a summary, motivation, and testing notes.
98
+ 6. **Review** – address reviewer comments; once approved and CI is green, a maintainer will merge.
99
+
100
+ PRs that break existing tests or skip the pre-commit hooks will not be merged.
101
+
102
+ ## Coding Style & Tests
103
+
104
+ ### Python
105
+ - **Formatter**: [Black](https://black.readthedocs.io/) (enforced automatically via pre-commit).
106
+ - **Linter**: [Ruff](https://docs.astral.sh/ruff/) with `--fix` (enforced automatically via pre-commit).
107
+ - **Docstrings**: [NumPy docstring style](https://numpydoc.readthedocs.io/en/latest/format.html).
108
+ Example:
109
+ ```python
110
+ def my_function(x: float, y: float) -> float:
111
+ """Short one-line summary.
112
+
113
+ Longer description if needed.
114
+
115
+ Parameters
116
+ ----------
117
+ x : float
118
+ Description of x.
119
+ y : float
120
+ Description of y.
121
+
122
+ Returns
123
+ -------
124
+ float
125
+ Description of the return value.
126
+ """
127
+ ```
128
+ - **Type annotations**: use `from __future__ import annotations` at the top of every module and annotate all public functions and methods.
129
+ - **Tests**: place tests under `tests/` and use [pytest](https://docs.pytest.org/). Run with `pytest`.
130
+
131
+ ### Rust
132
+ - **Formatter**: `cargo fmt --all` (enforced automatically via pre-commit).
133
+ - **Tests**: unit tests live alongside the source (`#[cfg(test)]` blocks). Run with `cargo test --lib`.
134
+
135
+ All checks are wired up via pre-commit and run automatically on every commit. You can also run them manually:
136
+ ```bash
137
+ pre-commit run --all-files
138
+ ```
139
+
140
+ ## Writing Documentation
141
+ Documentation lives under `docs/` and is built with [Sphinx](https://www.sphinx-doc.org/).
142
+
143
+ - Public API is documented via **NumPy-style docstrings** (see above) — Sphinx picks these up automatically.
144
+ - Narrative docs (tutorials, how-tos) go in `.rst` files under `docs/`.
145
+ - Build locally with:
146
+ ```bash
147
+ cd docs
148
+ make html
149
+ # open _build/html/index.html in your browser
150
+ ```
151
+ - Keep prose clear and concise. Include runnable code examples where possible.
152
+
153
+ ## Where to Get Help
154
+ | Channel | When to use |
155
+ |---------|-------------|
156
+ | [Documentation](https://multimoda-rs.readthedocs.io/en/latest/index.html) | First stop, API reference and tutorials |
157
+ | [GitHub Issues](https://github.com/yungselm/multimoda-rs/issues) | Bug reports and feature requests |
158
+ | [GitHub Discussions](https://github.com/yungselm/multimoda-rs/discussions) | Questions, ideas, and general discussion |
159
+
160
+ When asking for help, please include your `multimoda-rs` version, Python version, OS, and a minimal reproducible example.
161
+
162
+
163
+ **ฅ^>⩊<^ ฅ**
164
+ ------------------------------------------------------------------------------------
@@ -161,12 +161,6 @@ version = "1.23.1"
161
161
  source = "registry+https://github.com/rust-lang/crates.io-index"
162
162
  checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422"
163
163
 
164
- [[package]]
165
- name = "byteorder"
166
- version = "1.5.0"
167
- source = "registry+https://github.com/rust-lang/crates.io-index"
168
- checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
169
-
170
164
  [[package]]
171
165
  name = "byteorder-lite"
172
166
  version = "0.1.0"
@@ -390,15 +384,6 @@ dependencies = [
390
384
  "miniz_oxide",
391
385
  ]
392
386
 
393
- [[package]]
394
- name = "float-cmp"
395
- version = "0.10.0"
396
- source = "registry+https://github.com/rust-lang/crates.io-index"
397
- checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8"
398
- dependencies = [
399
- "num-traits",
400
- ]
401
-
402
387
  [[package]]
403
388
  name = "getrandom"
404
389
  version = "0.3.4"
@@ -726,7 +711,7 @@ dependencies = [
726
711
 
727
712
  [[package]]
728
713
  name = "multimodars"
729
- version = "0.2.2"
714
+ version = "0.3.0"
730
715
  dependencies = [
731
716
  "anyhow",
732
717
  "approx",
@@ -737,7 +722,6 @@ dependencies = [
737
722
  "pyo3",
738
723
  "rayon",
739
724
  "serde",
740
- "stl_io",
741
725
  "toml",
742
726
  ]
743
727
 
@@ -1255,16 +1239,6 @@ version = "1.2.1"
1255
1239
  source = "registry+https://github.com/rust-lang/crates.io-index"
1256
1240
  checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
1257
1241
 
1258
- [[package]]
1259
- name = "stl_io"
1260
- version = "0.10.0"
1261
- source = "registry+https://github.com/rust-lang/crates.io-index"
1262
- checksum = "da63e75b86345156b191c021b3ce2a13b973941ecdb8c70d6f00cbbfe0076ed7"
1263
- dependencies = [
1264
- "byteorder",
1265
- "float-cmp",
1266
- ]
1267
-
1268
1242
  [[package]]
1269
1243
  name = "syn"
1270
1244
  version = "2.0.104"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "multimodars"
3
- version = "0.2.2"
3
+ version = "0.3.0"
4
4
  edition = "2021"
5
5
  license = "MIT"
6
6
  readme = "README.md"
@@ -20,4 +20,3 @@ rayon = "1.7.1"
20
20
  crossbeam = "0.8.4"
21
21
  anyhow = "1.0.100"
22
22
  approx = "0.5"
23
- stl_io = "0.10.0"
@@ -1,30 +1,35 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multimodars
3
- Version: 0.2.2
3
+ Version: 0.3.0
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
7
7
  Classifier: License :: OSI Approved :: MIT License
8
8
  Requires-Dist: numpy>=2.0.0,<3.0.0
9
9
  Requires-Dist: trimesh>=4.8.0
10
- Requires-Dist: pyglet<2
10
+ Requires-Dist: scipy>=1.15.0
11
11
  Requires-Dist: sphinx>=8.0,<9.0 ; extra == 'docs'
12
12
  Requires-Dist: sphinx-rtd-theme ; extra == 'docs'
13
13
  Requires-Dist: sphinx-autodoc-typehints ; extra == 'docs'
14
14
  Requires-Dist: myst-parser ; extra == 'docs'
15
15
  Requires-Dist: maturin>=1.7,<2.0 ; extra == 'docs'
16
16
  Requires-Dist: furo ; extra == 'docs'
17
+ Requires-Dist: pymeshlab>=2023.12 ; extra == 'meshlab'
17
18
  Requires-Dist: pytest>=9.0.2 ; extra == 'test'
18
19
  Requires-Dist: pytest-cov ; extra == 'test'
19
20
  Requires-Dist: ruff ; extra == 'test'
20
21
  Requires-Dist: black ; extra == 'test'
22
+ Requires-Dist: mypy ; extra == 'test'
23
+ Requires-Dist: pyglet<2 ; extra == 'viz'
21
24
  Provides-Extra: docs
25
+ Provides-Extra: meshlab
22
26
  Provides-Extra: test
27
+ Provides-Extra: viz
23
28
  License-File: LICENSE
24
29
  Summary: A Rust-powered cardiac multi-image modality fusion package
25
30
  Author: Anselm W. Stark
26
31
  License-Expression: MIT
27
- Requires-Python: >=3.8
32
+ Requires-Python: >=3.10
28
33
  Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
29
34
  Project-URL: Homepage, https://github.com/yungselm/multimoda-rs
30
35
  Project-URL: Issues, https://github.com/yungselm/multimoda-rs/issues
@@ -71,7 +76,7 @@ Project-URL: Issues, https://github.com/yungselm/multimoda-rs/issues
71
76
  - Centerline Alignment
72
77
  - Align registered geometries onto a vessel centerline using three‑point or manual rotation methods.
73
78
  - Geometry Utilities
74
- - Smooth contours, reorder frames to minimize spatial and index jumps, compute areas and elliptic ratios, find farthest/closest point pairs, and more.
79
+ - Smooth contours, compute areas and elliptic ratios, find farthest/closest point pairs, and more.
75
80
 
76
81
  ## Installation
77
82
 
@@ -106,7 +111,8 @@ maturin develop
106
111
 
107
112
 
108
113
  ## Quickstart Example
109
- Download the example data from the latest release and place it in your working directory. A Jupyter Notebook with step-by-step examples is available at `examples/ivus_to_centerline.ipynb`.
114
+ Download [examples.zip](https://github.com/yungselm/multimoda-rs/releases/latest/download/examples.zip) (SHA256: `d11ebc7607f43ab4571fb51c9ac9178caac57774cf5d97f4f068ace4eb070fee`) from the latest release and place it in your working directory. A Jupyter Notebook with step-by-step examples is available at `examples/ivus_to_centerline.ipynb`.
115
+ Provided you extraced examples to the project root you can just copy paste the below example to ensure proper setup:
110
116
 
111
117
  ```python
112
118
  import multimodars as mm
@@ -114,8 +120,8 @@ import numpy as np
114
120
 
115
121
  # IVUS pullbacks: full alignment of rest/stress & diastole/systole
116
122
  rest, stress, dia, sys, _ = mm.from_file_full(
117
- input_path_a="ivus_rest",
118
- input_path_b="ivus_stress",
123
+ input_path_a="examples/data/ivus_rest",
124
+ input_path_b="examples/data/ivus_stress",
119
125
  label="full",
120
126
  step_rotation_deg=0.1,
121
127
  range_rotation_deg=90,
@@ -128,12 +134,12 @@ rest, stress, dia, sys, _ = mm.from_file_full(
128
134
  output_path_b="output/stress",
129
135
  output_path_c="output/diastole",
130
136
  output_path_d="output/systole",
131
- interpolation_steps=28,
137
+ interpolation_steps=0,
132
138
  contour_types=[mm.PyContourType.Lumen, mm.PyContourType.Catheter, mm.PyContourType.Wall]
133
139
  )
134
140
 
135
141
  # Load raw centerline and align geometry onto it
136
- cl_raw = np.genfromtxt("centerline_raw.csv", delimiter=",")
142
+ cl_raw = np.genfromtxt("examples/data/centerline_raw.csv", delimiter=",")
137
143
  centerline = mm.numpy_to_centerline(cl_raw)
138
144
 
139
145
  aligned_pair, cl_resampled = mm.align_three_point(
@@ -40,7 +40,7 @@
40
40
  - Centerline Alignment
41
41
  - Align registered geometries onto a vessel centerline using three‑point or manual rotation methods.
42
42
  - Geometry Utilities
43
- - Smooth contours, reorder frames to minimize spatial and index jumps, compute areas and elliptic ratios, find farthest/closest point pairs, and more.
43
+ - Smooth contours, compute areas and elliptic ratios, find farthest/closest point pairs, and more.
44
44
 
45
45
  ## Installation
46
46
 
@@ -75,7 +75,8 @@ maturin develop
75
75
 
76
76
 
77
77
  ## Quickstart Example
78
- Download the example data from the latest release and place it in your working directory. A Jupyter Notebook with step-by-step examples is available at `examples/ivus_to_centerline.ipynb`.
78
+ Download [examples.zip](https://github.com/yungselm/multimoda-rs/releases/latest/download/examples.zip) (SHA256: `d11ebc7607f43ab4571fb51c9ac9178caac57774cf5d97f4f068ace4eb070fee`) from the latest release and place it in your working directory. A Jupyter Notebook with step-by-step examples is available at `examples/ivus_to_centerline.ipynb`.
79
+ Provided you extraced examples to the project root you can just copy paste the below example to ensure proper setup:
79
80
 
80
81
  ```python
81
82
  import multimodars as mm
@@ -83,8 +84,8 @@ import numpy as np
83
84
 
84
85
  # IVUS pullbacks: full alignment of rest/stress & diastole/systole
85
86
  rest, stress, dia, sys, _ = mm.from_file_full(
86
- input_path_a="ivus_rest",
87
- input_path_b="ivus_stress",
87
+ input_path_a="examples/data/ivus_rest",
88
+ input_path_b="examples/data/ivus_stress",
88
89
  label="full",
89
90
  step_rotation_deg=0.1,
90
91
  range_rotation_deg=90,
@@ -97,12 +98,12 @@ rest, stress, dia, sys, _ = mm.from_file_full(
97
98
  output_path_b="output/stress",
98
99
  output_path_c="output/diastole",
99
100
  output_path_d="output/systole",
100
- interpolation_steps=28,
101
+ interpolation_steps=0,
101
102
  contour_types=[mm.PyContourType.Lumen, mm.PyContourType.Catheter, mm.PyContourType.Wall]
102
103
  )
103
104
 
104
105
  # Load raw centerline and align geometry onto it
105
- cl_raw = np.genfromtxt("centerline_raw.csv", delimiter=",")
106
+ cl_raw = np.genfromtxt("examples/data/centerline_raw.csv", delimiter=",")
106
107
  centerline = mm.numpy_to_centerline(cl_raw)
107
108
 
108
109
  aligned_pair, cl_resampled = mm.align_three_point(