multimodars 0.4.2__tar.gz → 0.4.3__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 (151) hide show
  1. {multimodars-0.4.2 → multimodars-0.4.3}/CHANGELOG.md +7 -0
  2. {multimodars-0.4.2 → multimodars-0.4.3}/Cargo.lock +1 -1
  3. {multimodars-0.4.2 → multimodars-0.4.3}/Cargo.toml +1 -1
  4. {multimodars-0.4.2 → multimodars-0.4.3}/PKG-INFO +1 -1
  5. {multimodars-0.4.2 → multimodars-0.4.3}/src/ccta/adjust_mesh/scale_coronary.rs +28 -28
  6. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/binding/entry.rs +4 -28
  7. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/centerline_align/align_algorithms.rs +4 -4
  8. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/processing/align_between.rs +4 -4
  9. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/processing/align_within.rs +8 -17
  10. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/processing/postprocessing.rs +5 -5
  11. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/processing/process_utils.rs +25 -74
  12. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/to_object/write_mtl.rs +3 -28
  13. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/binding/py_geometry.rs +2 -3
  14. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/native/contour_point.rs +73 -1
  15. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/native.rs +1 -1
  16. {multimodars-0.4.2 → multimodars-0.4.3}/.gitattributes +0 -0
  17. {multimodars-0.4.2 → multimodars-0.4.3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  18. {multimodars-0.4.2 → multimodars-0.4.3}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  19. {multimodars-0.4.2 → multimodars-0.4.3}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  20. {multimodars-0.4.2 → multimodars-0.4.3}/.github/workflows/CI.yml +0 -0
  21. {multimodars-0.4.2 → multimodars-0.4.3}/.gitignore +0 -0
  22. {multimodars-0.4.2 → multimodars-0.4.3}/.pre-commit-config.yaml +0 -0
  23. {multimodars-0.4.2 → multimodars-0.4.3}/.readthedocs.yaml +0 -0
  24. {multimodars-0.4.2 → multimodars-0.4.3}/CITATION.cff +0 -0
  25. {multimodars-0.4.2 → multimodars-0.4.3}/CONTRIBUTING.md +0 -0
  26. {multimodars-0.4.2 → multimodars-0.4.3}/LICENSE +0 -0
  27. {multimodars-0.4.2 → multimodars-0.4.3}/README.md +0 -0
  28. {multimodars-0.4.2 → multimodars-0.4.3}/ROADMAP.MD +0 -0
  29. {multimodars-0.4.2 → multimodars-0.4.3}/benchmarks/BENCHMARK_PLAN.md +0 -0
  30. {multimodars-0.4.2 → multimodars-0.4.3}/benchmarks/benchmark_bruteforce_stepsize.py +0 -0
  31. {multimodars-0.4.2 → multimodars-0.4.3}/benchmarks/benchmark_cpu_scaling.py +0 -0
  32. {multimodars-0.4.2 → multimodars-0.4.3}/benchmarks/results/bruteforce_stepsize.png +0 -0
  33. {multimodars-0.4.2 → multimodars-0.4.3}/benchmarks/results/cpu_scaling.png +0 -0
  34. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/idealized_geometry/branch_diastolic_contours.csv +0 -0
  35. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/idealized_geometry/branch_systolic_contours.csv +0 -0
  36. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/idealized_geometry/calcium_diastolic_contours.csv +0 -0
  37. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/idealized_geometry/calcium_systolic_contours.csv +0 -0
  38. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/idealized_geometry/diastolic_contours.csv +0 -0
  39. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/idealized_geometry/diastolic_reference_points.csv +0 -0
  40. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/idealized_geometry/eem_diastolic_contours.csv +0 -0
  41. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/idealized_geometry/eem_systolic_contours.csv +0 -0
  42. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/idealized_geometry/systolic_contours.csv +0 -0
  43. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/idealized_geometry/systolic_reference_points.csv +0 -0
  44. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/ivus_full/branch_diastolic_contours.csv +0 -0
  45. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/ivus_full/branch_systolic_contours.csv +0 -0
  46. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/ivus_full/calcium_diastolic_contours.csv +0 -0
  47. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/ivus_full/calcium_systolic_contours.csv +0 -0
  48. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/ivus_full/diastolic_contours.csv +0 -0
  49. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/ivus_full/diastolic_reference_points.csv +0 -0
  50. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/ivus_full/eem_diastolic_contours.csv +0 -0
  51. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/ivus_full/eem_systolic_contours.csv +0 -0
  52. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/ivus_full/systolic_contours.csv +0 -0
  53. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/ivus_full/systolic_reference_points.csv +0 -0
  54. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/ivus_rest/combined_sorted_manual.csv +0 -0
  55. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/ivus_rest/diastolic_contours.csv +0 -0
  56. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/ivus_rest/diastolic_reference_points.csv +0 -0
  57. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/ivus_rest/systolic_contours.csv +0 -0
  58. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/ivus_rest/systolic_reference_points.csv +0 -0
  59. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/ivus_stress/combined_sorted_manual.csv +0 -0
  60. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/ivus_stress/diastolic_contours.csv +0 -0
  61. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/ivus_stress/diastolic_reference_points.csv +0 -0
  62. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/ivus_stress/systolic_contours.csv +0 -0
  63. {multimodars-0.4.2 → multimodars-0.4.3}/data/fixtures/ivus_stress/systolic_reference_points.csv +0 -0
  64. {multimodars-0.4.2 → multimodars-0.4.3}/media/coronary_artery_disease.jpg +0 -0
  65. {multimodars-0.4.2 → multimodars-0.4.3}/media/dynamic_lumen_changes.png +0 -0
  66. {multimodars-0.4.2 → multimodars-0.4.3}/media/multimoda-rs.jpg +0 -0
  67. {multimodars-0.4.2 → multimodars-0.4.3}/multimodars/__init__.py +0 -0
  68. {multimodars-0.4.2 → multimodars-0.4.3}/multimodars/_converters.py +0 -0
  69. {multimodars-0.4.2 → multimodars-0.4.3}/multimodars/_processing.py +0 -0
  70. {multimodars-0.4.2 → multimodars-0.4.3}/multimodars/ccta/__init__.py +0 -0
  71. {multimodars-0.4.2 → multimodars-0.4.3}/multimodars/ccta/debug_plots.py +0 -0
  72. {multimodars-0.4.2 → multimodars-0.4.3}/multimodars/ccta/discretization_map.py +0 -0
  73. {multimodars-0.4.2 → multimodars-0.4.3}/multimodars/ccta/fixing_functions.py +0 -0
  74. {multimodars-0.4.2 → multimodars-0.4.3}/multimodars/ccta/labeling.py +0 -0
  75. {multimodars-0.4.2 → multimodars-0.4.3}/multimodars/ccta/manipulating.py +0 -0
  76. {multimodars-0.4.2 → multimodars-0.4.3}/multimodars/io/__init__.py +0 -0
  77. {multimodars-0.4.2 → multimodars-0.4.3}/multimodars/io/read_geometrical.py +0 -0
  78. {multimodars-0.4.2 → multimodars-0.4.3}/multimodars/io/write_geometries.py +0 -0
  79. {multimodars-0.4.2 → multimodars-0.4.3}/multimodars/multimodars.pyi +0 -0
  80. {multimodars-0.4.2 → multimodars-0.4.3}/multimodars/py.typed +0 -0
  81. {multimodars-0.4.2 → multimodars-0.4.3}/paper/REVIEWER1.md +0 -0
  82. {multimodars-0.4.2 → multimodars-0.4.3}/paper/REVIEWER2.md +0 -0
  83. {multimodars-0.4.2 → multimodars-0.4.3}/paper/bibliography.bib +0 -0
  84. {multimodars-0.4.2 → multimodars-0.4.3}/paper/figures/Figure1.jpg +0 -0
  85. {multimodars-0.4.2 → multimodars-0.4.3}/paper/figures/Figure2.jpg +0 -0
  86. {multimodars-0.4.2 → multimodars-0.4.3}/paper/figures/Figure3.png +0 -0
  87. {multimodars-0.4.2 → multimodars-0.4.3}/paper/figures/Figure4.jpg +0 -0
  88. {multimodars-0.4.2 → multimodars-0.4.3}/paper/paper.md +0 -0
  89. {multimodars-0.4.2 → multimodars-0.4.3}/pyproject.toml +0 -0
  90. {multimodars-0.4.2 → multimodars-0.4.3}/src/ccta/adjust_mesh/label_coronary.rs +0 -0
  91. {multimodars-0.4.2 → multimodars-0.4.3}/src/ccta/adjust_mesh.rs +0 -0
  92. {multimodars-0.4.2 → multimodars-0.4.3}/src/ccta/binding/ccta_py.rs +0 -0
  93. {multimodars-0.4.2 → multimodars-0.4.3}/src/ccta/binding.rs +0 -0
  94. {multimodars-0.4.2 → multimodars-0.4.3}/src/ccta/discretizing/plan.md +0 -0
  95. {multimodars-0.4.2 → multimodars-0.4.3}/src/ccta/discretizing/projecting.rs +0 -0
  96. {multimodars-0.4.2 → multimodars-0.4.3}/src/ccta/discretizing/resampling.rs +0 -0
  97. {multimodars-0.4.2 → multimodars-0.4.3}/src/ccta/discretizing/utils.rs +0 -0
  98. {multimodars-0.4.2 → multimodars-0.4.3}/src/ccta/discretizing/vessel_tree.rs +0 -0
  99. {multimodars-0.4.2 → multimodars-0.4.3}/src/ccta/discretizing.rs +0 -0
  100. {multimodars-0.4.2 → multimodars-0.4.3}/src/ccta.rs +0 -0
  101. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/binding/align.rs +0 -0
  102. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/binding/functions.rs +0 -0
  103. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/binding.rs +0 -0
  104. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/centerline_align/align.rs +0 -0
  105. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/centerline_align/preprocessing.rs +0 -0
  106. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/centerline_align.rs +0 -0
  107. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/io/build.rs +0 -0
  108. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/io/geometry.rs +0 -0
  109. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/io/input.rs +0 -0
  110. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/io/integrity_check.rs +0 -0
  111. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/io/output.rs +0 -0
  112. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/io.rs +0 -0
  113. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/processing/preprocessing.rs +0 -0
  114. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/processing/wall.rs +0 -0
  115. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/processing.rs +0 -0
  116. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/to_object/interpolation.rs +0 -0
  117. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/to_object/process.rs +0 -0
  118. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/to_object/texture.rs +0 -0
  119. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/to_object.rs +0 -0
  120. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/utils/general_utils.rs +0 -0
  121. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/utils/test_utils.rs +0 -0
  122. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular/utils.rs +0 -0
  123. {multimodars-0.4.2 → multimodars-0.4.3}/src/intravascular.rs +0 -0
  124. {multimodars-0.4.2 → multimodars-0.4.3}/src/lib.rs +0 -0
  125. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/binding/py_centerline.rs +0 -0
  126. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/binding/py_centerline_point.rs +0 -0
  127. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/binding/py_contour.rs +0 -0
  128. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/binding/py_contour_point.rs +0 -0
  129. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/binding/py_discretized_vessel_tree.rs +0 -0
  130. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/binding/py_frame.rs +0 -0
  131. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/binding/py_geometry_pair.rs +0 -0
  132. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/binding/py_input_data.rs +0 -0
  133. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/binding/py_record.rs +0 -0
  134. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/binding.rs +0 -0
  135. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/native/centerline.rs +0 -0
  136. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/native/centerline_point.rs +0 -0
  137. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/native/contour.rs +0 -0
  138. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/native/discretized_tree.rs +0 -0
  139. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/native/frame.rs +0 -0
  140. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/native/geometry.rs +0 -0
  141. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/native/geometry_pair.rs +0 -0
  142. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/native/input_data.rs +0 -0
  143. {multimodars-0.4.2 → multimodars-0.4.3}/src/types/native/record.rs +0 -0
  144. {multimodars-0.4.2 → multimodars-0.4.3}/src/types.rs +0 -0
  145. {multimodars-0.4.2 → multimodars-0.4.3}/tests/conftest.py +0 -0
  146. {multimodars-0.4.2 → multimodars-0.4.3}/tests/test_ccta.py +0 -0
  147. {multimodars-0.4.2 → multimodars-0.4.3}/tests/test_converters.py +0 -0
  148. {multimodars-0.4.2 → multimodars-0.4.3}/tests/test_core.py +0 -0
  149. {multimodars-0.4.2 → multimodars-0.4.3}/tests/test_intravascular.py +0 -0
  150. {multimodars-0.4.2 → multimodars-0.4.3}/tests/test_wrappers.py +0 -0
  151. {multimodars-0.4.2 → multimodars-0.4.3}/tests/tests_todo.md +0 -0
@@ -3,6 +3,13 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  This project adheres to [Semantic Versioning](https://semver.org/).
5
5
 
6
+ ## [0.4.3] - 2026-06-04
7
+ Small refactor based on cargo modules analysis (https://github.com/regexident/cargo-modules)
8
+
9
+ ### Changed
10
+ - Moved downsample points to top layer module `types`
11
+ - Renamed duplicate functions and names
12
+
6
13
  ## [0.4.2] - 2026-06-04
7
14
  Large internal refactor to improve readability and decouple modules. No behaviour change.
8
15
 
@@ -665,7 +665,7 @@ dependencies = [
665
665
 
666
666
  [[package]]
667
667
  name = "multimodars"
668
- version = "0.4.2"
668
+ version = "0.4.3"
669
669
  dependencies = [
670
670
  "anyhow",
671
671
  "approx",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "multimodars"
3
- version = "0.4.2"
3
+ version = "0.4.3"
4
4
  edition = "2021"
5
5
  license = "MIT"
6
6
  readme = "README.md"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multimodars
3
- Version: 0.4.2
3
+ Version: 0.4.3
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -2,12 +2,12 @@ use crate::types::native::{Centerline, CenterlinePoint, Frame};
2
2
  use rayon::prelude::*;
3
3
  use std::collections::HashSet;
4
4
 
5
- type Point3D = (f64, f64, f64);
5
+ type Coords3 = (f64, f64, f64);
6
6
 
7
7
  pub fn centerline_based_wall_diameter_optimization(
8
8
  centerline: &Centerline,
9
- ref_point_coronary: &Point3D,
10
- aortic_points: &[Point3D],
9
+ ref_point_coronary: &Coords3,
10
+ aortic_points: &[Coords3],
11
11
  ) -> f64 {
12
12
  if centerline.points.is_empty() || aortic_points.is_empty() {
13
13
  return 0.0;
@@ -25,7 +25,7 @@ pub fn centerline_based_wall_diameter_optimization(
25
25
  None => return 0.0,
26
26
  };
27
27
 
28
- let closest_aortic: &Point3D = match aortic_points.iter().min_by(|a, b| {
28
+ let closest_aortic: &Coords3 = match aortic_points.iter().min_by(|a, b| {
29
29
  let dist_a = super::calculate_squared_distance(*a, ref_point_coronary);
30
30
  let dist_b = super::calculate_squared_distance(*b, ref_point_coronary);
31
31
  dist_a
@@ -66,8 +66,8 @@ pub fn centerline_based_wall_diameter_optimization(
66
66
  }
67
67
 
68
68
  pub fn centerline_based_aortic_diameter_optimization(
69
- intramural_points: &[Point3D],
70
- reference_points: &[Point3D],
69
+ intramural_points: &[Coords3],
70
+ reference_points: &[Coords3],
71
71
  centerline: &Centerline,
72
72
  ) -> f64 {
73
73
  let start = -2.0f64;
@@ -91,12 +91,12 @@ pub fn centerline_based_aortic_diameter_optimization(
91
91
  }
92
92
 
93
93
  pub fn centerline_based_diameter_optimization(
94
- anomalous_points: &[Point3D],
94
+ anomalous_points: &[Coords3],
95
95
  n_proximal: usize,
96
96
  n_distal: usize,
97
97
  centerline: &Centerline,
98
- proximal_reference: &[Point3D],
99
- distal_reference: &[Point3D],
98
+ proximal_reference: &[Coords3],
99
+ distal_reference: &[Coords3],
100
100
  ) -> (f64, f64) {
101
101
  let (proximal_points, anomalous_points_new) =
102
102
  find_region_points(anomalous_points, proximal_reference, n_proximal);
@@ -134,10 +134,10 @@ pub fn centerline_based_diameter_optimization(
134
134
  }
135
135
 
136
136
  fn find_region_points(
137
- anomalous_points: &[Point3D],
138
- reference_points: &[Point3D],
137
+ anomalous_points: &[Coords3],
138
+ reference_points: &[Coords3],
139
139
  n_points: usize,
140
- ) -> (Vec<Point3D>, Vec<Point3D>) {
140
+ ) -> (Vec<Coords3>, Vec<Coords3>) {
141
141
  if anomalous_points.is_empty() || reference_points.is_empty() || n_points == 0 {
142
142
  return (Vec::new(), anomalous_points.to_vec());
143
143
  }
@@ -165,12 +165,12 @@ fn find_region_points(
165
165
 
166
166
  let selected_indices: HashSet<usize> = selected_slice.iter().map(|(i, _)| *i).collect();
167
167
 
168
- let selected_points: Vec<Point3D> = selected_slice
168
+ let selected_points: Vec<Coords3> = selected_slice
169
169
  .iter()
170
170
  .map(|(i, _)| anomalous_points[*i])
171
171
  .collect();
172
172
 
173
- let remaining_points: Vec<Point3D> = anomalous_points
173
+ let remaining_points: Vec<Coords3> = anomalous_points
174
174
  .iter()
175
175
  .enumerate()
176
176
  .filter_map(|(i, pt)| {
@@ -188,7 +188,7 @@ fn find_region_points(
188
188
  /// Symmetric average nearest-neighbor distance between two point sets.
189
189
  /// Returns sqrt of average squared distance for readability (i.e., RMS of nearest neighbor distances).
190
190
  /// If either set is empty returns f64::INFINITY.
191
- fn symmetric_nn_distance(a: &[Point3D], b: &[Point3D]) -> f64 {
191
+ fn symmetric_nn_distance(a: &[Coords3], b: &[Coords3]) -> f64 {
192
192
  if a.is_empty() || b.is_empty() {
193
193
  return f64::INFINITY;
194
194
  }
@@ -220,9 +220,9 @@ fn symmetric_nn_distance(a: &[Point3D], b: &[Point3D]) -> f64 {
220
220
 
221
221
  pub fn centerline_based_diameter_morphing(
222
222
  centerline: &Centerline,
223
- points: &[Point3D],
223
+ points: &[Coords3],
224
224
  diameter_adjustment_mm: f64,
225
- ) -> Vec<Point3D> {
225
+ ) -> Vec<Coords3> {
226
226
  points
227
227
  .par_iter()
228
228
  .map(|point| {
@@ -257,7 +257,7 @@ pub fn centerline_based_diameter_morphing(
257
257
 
258
258
  fn find_closest_centerline_point_optimized(
259
259
  centerline: &Centerline,
260
- point: Point3D,
260
+ point: Coords3,
261
261
  ) -> &CenterlinePoint {
262
262
  let mut min_distance_squared = f64::MAX;
263
263
  let mut closest_point = &centerline.points[0];
@@ -276,24 +276,24 @@ fn find_closest_centerline_point_optimized(
276
276
  pub fn find_points_by_cl_region_rs(
277
277
  centerline: &Centerline,
278
278
  frames: &[Frame],
279
- points: &[Point3D],
280
- ) -> (Vec<Point3D>, Vec<Point3D>, Vec<Point3D>) {
279
+ points: &[Coords3],
280
+ ) -> (Vec<Coords3>, Vec<Coords3>, Vec<Coords3>) {
281
281
  let mut cumulative_z_dist_frames = 0.0;
282
282
  for i in 1..frames.len() {
283
283
  cumulative_z_dist_frames += (frames[i].centroid.2 - frames[i - 1].centroid.2).abs();
284
284
  }
285
285
  cumulative_z_dist_frames /= (frames.len() - 1) as f64;
286
286
 
287
- let centroids_to_match = frames.iter().map(|f| f.centroid).collect::<Vec<Point3D>>();
287
+ let centroids_to_match = frames.iter().map(|f| f.centroid).collect::<Vec<Coords3>>();
288
288
  let cl_points_indices: Vec<usize> =
289
289
  find_cl_points_in_range(centerline, &centroids_to_match, cumulative_z_dist_frames);
290
290
 
291
291
  // needed for proximal/distal classification
292
292
  let dist_ref = centroids_to_match[centroids_to_match.len() - 1];
293
293
 
294
- let mut proximal_points: Vec<Point3D> = Vec::new();
295
- let mut distal_points: Vec<Point3D> = Vec::new();
296
- let mut points_between: Vec<Point3D> = Vec::new();
294
+ let mut proximal_points: Vec<Coords3> = Vec::new();
295
+ let mut distal_points: Vec<Coords3> = Vec::new();
296
+ let mut points_between: Vec<Coords3> = Vec::new();
297
297
 
298
298
  let mut remaining_points = points.to_vec();
299
299
 
@@ -327,7 +327,7 @@ pub fn find_points_by_cl_region_rs(
327
327
 
328
328
  fn find_cl_points_in_range(
329
329
  centerline: &Centerline,
330
- points: &[Point3D],
330
+ points: &[Coords3],
331
331
  search_radius: f64,
332
332
  ) -> Vec<usize> {
333
333
  let mut selected_points = Vec::new();
@@ -355,11 +355,11 @@ fn find_cl_points_in_range(
355
355
  }
356
356
 
357
357
  pub fn clean_up_non_section_points(
358
- points_to_cleanup: Vec<Point3D>,
359
- reference_points: Vec<Point3D>,
358
+ points_to_cleanup: Vec<Coords3>,
359
+ reference_points: Vec<Coords3>,
360
360
  neighborhood_radius: f64,
361
361
  min_neigbor_ratio: f64,
362
- ) -> (Vec<Point3D>, Vec<Point3D>) {
362
+ ) -> (Vec<Coords3>, Vec<Coords3>) {
363
363
  let neighborhood_radius_sq = neighborhood_radius * neighborhood_radius;
364
364
 
365
365
  let mut cleaned_points = Vec::new();
@@ -4,7 +4,7 @@ use std::fs::File;
4
4
  use std::io::Write;
5
5
  use std::path::Path;
6
6
 
7
- use crate::intravascular::io::geometry::{Contour, ContourType, Geometry};
7
+ use crate::intravascular::io::geometry::{ContourType, Geometry};
8
8
  use crate::intravascular::io::input::InputData;
9
9
  use crate::intravascular::io::output;
10
10
  use crate::intravascular::processing::align_between;
@@ -778,33 +778,9 @@ pub fn single_processing_rs(
778
778
  Ok((geom, logs))
779
779
  }
780
780
 
781
- /// Helper function to extract contours of a specific type from a geometry
782
- pub fn extract_contours_by_type(geometry: &Geometry, contour_type: ContourType) -> Vec<Contour> {
783
- match contour_type {
784
- ContourType::Lumen => geometry
785
- .frames
786
- .iter()
787
- .map(|frame| frame.lumen.clone())
788
- .collect(),
789
- _ => geometry
790
- .frames
791
- .iter()
792
- .filter_map(|frame| frame.extras.get(&contour_type).cloned())
793
- .collect(),
794
- }
795
- }
796
-
797
- /// Helper function to get contour type name for file naming
798
- pub fn get_contour_type_name(contour_type: ContourType) -> &'static str {
799
- match contour_type {
800
- ContourType::Lumen => "lumen",
801
- ContourType::Eem => "eem",
802
- ContourType::Calcification => "calcification",
803
- ContourType::Sidebranch => "sidebranch",
804
- ContourType::Catheter => "catheter",
805
- ContourType::Wall => "wall",
806
- }
807
- }
781
+ pub use crate::intravascular::processing::process_utils::{
782
+ extract_contours_by_type, get_contour_type_name,
783
+ };
808
784
 
809
785
  /// Helper function to create appropriate MTL file for each contour type
810
786
  pub fn create_mtl_for_contour_type(
@@ -2,9 +2,8 @@ use crate::intravascular::io::geometry::{Contour, Geometry};
2
2
  use crate::intravascular::io::input::ContourPoint;
3
3
  use crate::intravascular::io::input::{Centerline, CenterlinePoint};
4
4
  use crate::intravascular::processing::align_between::GeometryPair;
5
- use crate::intravascular::processing::process_utils::{
6
- downsample_contour_points, hausdorff_distance,
7
- };
5
+ use crate::intravascular::processing::process_utils::hausdorff_distance;
6
+ use crate::types::native;
8
7
  use nalgebra::{Point3, Rotation3, Unit, Vector3};
9
8
 
10
9
  /// Allows alignment algorithms to operate on either a single [`Geometry`] or a [`GeometryPair`].
@@ -408,7 +407,8 @@ pub fn refine_alignment_hausdorff<T: AlignTarget>(
408
407
 
409
408
  for frame in frames.iter() {
410
409
  if n_downsample < n_points_per_frame {
411
- let downsampled = downsample_contour_points(&frame.lumen.points, n_downsample);
410
+ let downsampled =
411
+ native::downsample_contour_points(&frame.lumen.points, n_downsample);
412
412
  nested.push(downsampled);
413
413
  } else {
414
414
  nested.push(frame.lumen.points.clone());
@@ -3,9 +3,8 @@ use rayon::prelude::*;
3
3
 
4
4
  use crate::intravascular::io::geometry::Geometry;
5
5
  use crate::intravascular::io::input::ContourPoint;
6
- use crate::intravascular::processing::process_utils::{
7
- downsample_contour_points, hausdorff_distance, search_range,
8
- };
6
+ use crate::intravascular::processing::process_utils::{hausdorff_distance, search_range};
7
+ use crate::types::native;
9
8
 
10
9
  pub use crate::types::native::GeometryPair;
11
10
 
@@ -163,7 +162,8 @@ fn extract_geometry_points_with_frame_info(
163
162
  let mut all_points = Vec::new();
164
163
  for frame in &geometry.frames {
165
164
  let frame_sample_size = (frame.lumen.points.len() as f64 * sample_ratio).ceil() as usize;
166
- let sampled = downsample_contour_points(&frame.lumen.points, frame_sample_size.max(1));
165
+ let sampled =
166
+ native::downsample_contour_points(&frame.lumen.points, frame_sample_size.max(1));
167
167
 
168
168
  for point in sampled {
169
169
  all_points.push(PointWithFrameInfo {
@@ -5,9 +5,8 @@ use std::sync::{Arc, Mutex};
5
5
  use super::wall;
6
6
  use crate::intravascular::io::geometry::{Contour, ContourType, Frame, Geometry};
7
7
  use crate::intravascular::io::input::ContourPoint;
8
- use crate::intravascular::processing::process_utils::{
9
- downsample_contour_points, hausdorff_distance, search_range,
10
- };
8
+ use crate::intravascular::processing::process_utils::{hausdorff_distance, search_range};
9
+ use crate::types::native;
11
10
 
12
11
  #[derive(Debug)]
13
12
  pub struct AlignLog {
@@ -170,10 +169,11 @@ fn catheter_lumen_vec_from_frames(
170
169
  sample_size_lumen: usize,
171
170
  sample_size_catheter: Option<usize>,
172
171
  ) -> Vec<ContourPoint> {
173
- let mut lumen_points = downsample_contour_points(&frame.lumen.points, sample_size_lumen);
172
+ let mut lumen_points =
173
+ native::downsample_contour_points(&frame.lumen.points, sample_size_lumen);
174
174
  let mut catheter_points = if let Some(sample_size_catheter) = sample_size_catheter {
175
175
  if let Some(catheter_contour) = frame.extras.get(&ContourType::Catheter) {
176
- downsample_contour_points(&catheter_contour.points, sample_size_catheter)
176
+ native::downsample_contour_points(&catheter_contour.points, sample_size_catheter)
177
177
  } else {
178
178
  Vec::new()
179
179
  }
@@ -562,13 +562,7 @@ fn interp_point(
562
562
  }
563
563
  }
564
564
 
565
- fn interpolate_contour(
566
- c1: &Contour,
567
- c2: &Contour,
568
- t: f64,
569
- id: u32,
570
- original_frame: u32,
571
- ) -> Contour {
565
+ fn fill_frame_gap(c1: &Contour, c2: &Contour, t: f64, id: u32, original_frame: u32) -> Contour {
572
566
  let len = c1.points.len().min(c2.points.len());
573
567
  let points: Vec<ContourPoint> = (0..len)
574
568
  .map(|i| interp_point(&c1.points[i], &c2.points[i], t, original_frame, i as u32))
@@ -601,7 +595,7 @@ fn create_interpolated_frame(frame_1: &Frame, frame_2: &Frame, t: f64) -> Frame
601
595
  frame_1.centroid.2 + (frame_2.centroid.2 - frame_1.centroid.2) * t,
602
596
  );
603
597
 
604
- let lumen = interpolate_contour(
598
+ let lumen = fill_frame_gap(
605
599
  &frame_1.lumen,
606
600
  &frame_2.lumen,
607
601
  t,
@@ -616,10 +610,7 @@ fn create_interpolated_frame(frame_1: &Frame, frame_2: &Frame, t: f64) -> Frame
616
610
  }
617
611
  match (frame_1.extras.get(key), frame_2.extras.get(key)) {
618
612
  (Some(c1), Some(c2)) => {
619
- extras.insert(
620
- *key,
621
- interpolate_contour(c1, c2, t, c2.id, c2.original_frame),
622
- );
613
+ extras.insert(*key, fill_frame_gap(c1, c2, t, c2.id, c2.original_frame));
623
614
  }
624
615
  (Some(c1), None) => {
625
616
  extras.insert(*key, c1.clone());
@@ -226,7 +226,7 @@ fn new_frames_by_sample_rate(geometry: &Geometry, mut z_coords: Vec<f64>) -> Geo
226
226
  let t = (z_coord - lower.centroid.2) / (upper.centroid.2 - lower.centroid.2);
227
227
 
228
228
  // Interpolate lumen contour
229
- let new_lumen = interpolate_contour(&lower.lumen, &upper.lumen, t);
229
+ let new_lumen = blend_contour(&lower.lumen, &upper.lumen, t);
230
230
 
231
231
  // Interpolate extra contours
232
232
  let mut new_extras = HashMap::new();
@@ -241,7 +241,7 @@ fn new_frames_by_sample_rate(geometry: &Geometry, mut z_coords: Vec<f64>) -> Geo
241
241
  {
242
242
  if let (Some(l_extra), Some(u_extra)) = (lower.extras.get(kind), upper.extras.get(kind))
243
243
  {
244
- new_extras.insert(*kind, interpolate_contour(l_extra, u_extra, t));
244
+ new_extras.insert(*kind, blend_contour(l_extra, u_extra, t));
245
245
  }
246
246
  }
247
247
 
@@ -297,7 +297,7 @@ fn new_frames_by_sample_rate(geometry: &Geometry, mut z_coords: Vec<f64>) -> Geo
297
297
  }
298
298
  }
299
299
 
300
- fn interpolate_contour(c1: &Contour, c2: &Contour, t: f64) -> Contour {
300
+ fn blend_contour(c1: &Contour, c2: &Contour, t: f64) -> Contour {
301
301
  let new_points: Vec<ContourPoint> = c1
302
302
  .points
303
303
  .iter()
@@ -716,7 +716,7 @@ mod tests {
716
716
  }
717
717
 
718
718
  #[test]
719
- fn test_interpolate_contour() {
719
+ fn test_blend_contour() {
720
720
  let contour1 = create_test_contour(0, 0.0, None, ContourType::Lumen);
721
721
  let mut contour2 = create_test_contour(1, 2.0, None, ContourType::Lumen);
722
722
 
@@ -726,7 +726,7 @@ mod tests {
726
726
  contour2.points[1].x = 7.0;
727
727
  contour2.points[1].y = 8.0;
728
728
 
729
- let interpolated = interpolate_contour(&contour1, &contour2, 0.5);
729
+ let interpolated = blend_contour(&contour1, &contour2, 0.5);
730
730
 
731
731
  // Points should be interpolated - using the correct calculation
732
732
  // p1.x = 1.0, p2.x = 5.0, t=0.5 → 1.0 + 0.5*(5.0-1.0) = 3.0
@@ -1,20 +1,35 @@
1
+ use crate::intravascular::io::geometry::{Contour, ContourType, Geometry};
1
2
  use crate::intravascular::io::input::ContourPoint;
2
3
  use rayon::prelude::*;
3
4
  use std::f64::consts::PI;
4
5
 
5
- pub fn downsample_contour_points(points: &[ContourPoint], n: usize) -> Vec<ContourPoint> {
6
- if points.len() <= n {
7
- return points.to_vec();
6
+ pub fn extract_contours_by_type(geometry: &Geometry, contour_type: ContourType) -> Vec<Contour> {
7
+ match contour_type {
8
+ ContourType::Lumen => geometry
9
+ .frames
10
+ .iter()
11
+ .map(|frame| frame.lumen.clone())
12
+ .collect(),
13
+ _ => geometry
14
+ .frames
15
+ .iter()
16
+ .filter_map(|frame| frame.extras.get(&contour_type).cloned())
17
+ .collect(),
8
18
  }
9
- let step = points.len() as f64 / n as f64;
10
- (0..n)
11
- .map(|i| {
12
- let index = (i as f64 * step) as usize;
13
- points[index]
14
- })
15
- .collect()
16
19
  }
17
20
 
21
+ pub fn get_contour_type_name(contour_type: ContourType) -> &'static str {
22
+ match contour_type {
23
+ ContourType::Lumen => "lumen",
24
+ ContourType::Eem => "eem",
25
+ ContourType::Calcification => "calcification",
26
+ ContourType::Sidebranch => "sidebranch",
27
+ ContourType::Catheter => "catheter",
28
+ ContourType::Wall => "wall",
29
+ }
30
+ }
31
+
32
+
18
33
  pub fn search_range<F>(
19
34
  cost_fn: F,
20
35
  step_deg: f64,
@@ -110,72 +125,8 @@ fn directed_hausdorff(contour_a: &[ContourPoint], contour_b: &[ContourPoint]) ->
110
125
  #[cfg(test)]
111
126
  mod process_utils_tests {
112
127
  use super::*;
113
- use crate::intravascular::utils::test_utils::dummy_geometry;
114
128
  use approx::assert_relative_eq;
115
129
 
116
- #[test]
117
- fn test_downsample_geometry() {
118
- let dummy = dummy_geometry();
119
- let cont_points = dummy.frames[0].lumen.points.clone();
120
- let downsampled_contour = downsample_contour_points(&cont_points, 3);
121
-
122
- assert_eq!(downsampled_contour.len(), 3);
123
- assert_eq!(downsampled_contour[0].point_index, 0);
124
- assert_eq!(downsampled_contour[1].point_index, 2);
125
-
126
- let downsampled_contour = downsample_contour_points(&cont_points, 6);
127
-
128
- assert_eq!(downsampled_contour.len(), 6);
129
- assert_eq!(downsampled_contour[0].point_index, 0);
130
- assert_eq!(downsampled_contour[1].point_index, 1);
131
-
132
- let downsampled_contour = downsample_contour_points(&cont_points, 5);
133
- let n = downsampled_contour.len();
134
- assert_eq!(downsampled_contour[n - 1].point_index, 4);
135
- }
136
-
137
- // tests from here are AI generated!!
138
- #[test]
139
- fn test_downsample_edge_cases() {
140
- let points = vec![
141
- ContourPoint {
142
- frame_index: 1,
143
- point_index: 0,
144
- x: 1.0,
145
- y: 2.0,
146
- z: 0.0,
147
- aortic: false,
148
- },
149
- ContourPoint {
150
- frame_index: 1,
151
- point_index: 1,
152
- x: 3.0,
153
- y: 4.0,
154
- z: 0.0,
155
- aortic: false,
156
- },
157
- ];
158
-
159
- // Test n > points.len()
160
- let result = downsample_contour_points(&points, 5);
161
- assert_eq!(result.len(), 2);
162
- assert_eq!(result[0].point_index, 0);
163
- assert_eq!(result[1].point_index, 1);
164
-
165
- // Test n == points.len()
166
- let result = downsample_contour_points(&points, 2);
167
- assert_eq!(result.len(), 2);
168
-
169
- // Test n == 0
170
- let result = downsample_contour_points(&points, 0);
171
- assert_eq!(result.len(), 0);
172
-
173
- // Test empty input
174
- let empty: Vec<ContourPoint> = Vec::new();
175
- let result = downsample_contour_points(&empty, 3);
176
- assert_eq!(result.len(), 0);
177
- }
178
-
179
130
  #[test]
180
131
  fn test_search_range_quadratic_function() {
181
132
  // Test with a simple quadratic cost function: (angle - 0.5)^2
@@ -3,6 +3,9 @@ use super::texture::{
3
3
  create_displacement_texture, create_transparent_texture,
4
4
  };
5
5
  use crate::intravascular::io::geometry::{Contour, ContourType, Geometry};
6
+ use crate::intravascular::processing::process_utils::{
7
+ extract_contours_by_type, get_contour_type_name,
8
+ };
6
9
  use ::std::fs::File;
7
10
  use ::std::io::Write;
8
11
  use std::collections::HashMap;
@@ -246,22 +249,6 @@ fn write_transparent_texture(
246
249
  uv_coords
247
250
  }
248
251
 
249
- /// Extracts contours of a specific type from a geometry
250
- fn extract_contours_by_type(geometry: &Geometry, contour_type: ContourType) -> Vec<Contour> {
251
- match contour_type {
252
- ContourType::Lumen => geometry
253
- .frames
254
- .iter()
255
- .map(|frame| frame.lumen.clone())
256
- .collect(),
257
- _ => geometry
258
- .frames
259
- .iter()
260
- .filter_map(|frame| frame.extras.get(&contour_type).cloned())
261
- .collect(),
262
- }
263
- }
264
-
265
252
  /// Computes displacements between two sets of contours (alternative implementation)
266
253
  /// This is used for max displacement calculation since the texture.rs version only works on Geometry
267
254
  fn compute_displacements_for_contours(reference: &[Contour], target: &[Contour]) -> Vec<f64> {
@@ -283,15 +270,3 @@ fn compute_displacements_for_contours(reference: &[Contour], target: &[Contour])
283
270
  })
284
271
  .collect()
285
272
  }
286
-
287
- /// Gets the string name for a contour type for file naming
288
- fn get_contour_type_name(contour_type: ContourType) -> &'static str {
289
- match contour_type {
290
- ContourType::Lumen => "lumen",
291
- ContourType::Eem => "eem",
292
- ContourType::Calcification => "calcification",
293
- ContourType::Sidebranch => "sidebranch",
294
- ContourType::Catheter => "catheter",
295
- ContourType::Wall => "wall",
296
- }
297
- }
@@ -1,8 +1,7 @@
1
1
  use super::py_contour::{PyContour, PyContourType};
2
2
  use super::py_contour_point::PyContourPoint;
3
3
  use super::py_frame::PyFrame;
4
- use crate::intravascular::processing::process_utils;
5
- use crate::types::native::{ContourPoint, ContourType, Geometry};
4
+ use crate::types::native::{self as native, ContourPoint, ContourType, Geometry};
6
5
  use pyo3::prelude::*;
7
6
 
8
7
  type GeomSummary = (f64, f64, f64);
@@ -400,7 +399,7 @@ impl PyGeometry {
400
399
  let downsample_contour = |contour: &PyContour| -> PyContour {
401
400
  let rust_points: Vec<ContourPoint> =
402
401
  contour.points.iter().map(ContourPoint::from).collect();
403
- let downsampled = process_utils::downsample_contour_points(&rust_points, n_points);
402
+ let downsampled = native::downsample_contour_points(&rust_points, n_points);
404
403
  PyContour {
405
404
  points: downsampled.iter().map(PyContourPoint::from).collect(),
406
405
  ..contour.clone()