engeom 0.2.6__tar.gz → 0.2.8__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 (148) hide show
  1. {engeom-0.2.6 → engeom-0.2.8}/Cargo.lock +16 -15
  2. {engeom-0.2.6 → engeom-0.2.8}/Cargo.toml +3 -3
  3. {engeom-0.2.6 → engeom-0.2.8}/PKG-INFO +1 -1
  4. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/lib.rs +1 -0
  5. engeom-0.2.8/engeom/src/raster3.rs +60 -0
  6. {engeom-0.2.6 → engeom-0.2.8}/python/engeom/_plot/pyvista.py +34 -5
  7. {engeom-0.2.6 → engeom-0.2.8}/python/engeom/geom3.pyi +26 -0
  8. engeom-0.2.8/python/engeom/raster3/__init__.py +9 -0
  9. engeom-0.2.8/python/engeom/raster3.pyi +19 -0
  10. {engeom-0.2.6 → engeom-0.2.8}/src/lib.rs +16 -0
  11. {engeom-0.2.6 → engeom-0.2.8}/src/mesh.rs +11 -2
  12. engeom-0.2.8/src/raster.rs +40 -0
  13. engeom-0.2.8/src/ray_casting.rs +113 -0
  14. {engeom-0.2.6 → engeom-0.2.8}/.github/workflows/CI.yml +0 -0
  15. {engeom-0.2.6 → engeom-0.2.8}/.gitignore +0 -0
  16. {engeom-0.2.6 → engeom-0.2.8}/.gitmodules +0 -0
  17. {engeom-0.2.6 → engeom-0.2.8}/README.md +0 -0
  18. {engeom-0.2.6 → engeom-0.2.8}/docs/airfoils/intro.md +0 -0
  19. {engeom-0.2.6 → engeom-0.2.8}/docs/api/airfoil.md +0 -0
  20. {engeom-0.2.6 → engeom-0.2.8}/docs/api/engeom.md +0 -0
  21. {engeom-0.2.6 → engeom-0.2.8}/docs/api/geom2.md +0 -0
  22. {engeom-0.2.6 → engeom-0.2.8}/docs/api/geom3.md +0 -0
  23. {engeom-0.2.6 → engeom-0.2.8}/docs/api/metrology.md +0 -0
  24. {engeom-0.2.6 → engeom-0.2.8}/docs/api/plot.md +0 -0
  25. {engeom-0.2.6 → engeom-0.2.8}/docs/bounding_volumes.md +0 -0
  26. {engeom-0.2.6 → engeom-0.2.8}/docs/curves.md +0 -0
  27. {engeom-0.2.6 → engeom-0.2.8}/docs/images/surface_point_meas.svg +0 -0
  28. {engeom-0.2.6 → engeom-0.2.8}/docs/index.md +0 -0
  29. {engeom-0.2.6 → engeom-0.2.8}/docs/isometries.md +0 -0
  30. {engeom-0.2.6 → engeom-0.2.8}/docs/meshes.md +0 -0
  31. {engeom-0.2.6 → engeom-0.2.8}/docs/metrology.md +0 -0
  32. {engeom-0.2.6 → engeom-0.2.8}/docs/numpy.md +0 -0
  33. {engeom-0.2.6 → engeom-0.2.8}/docs/planes_circles_lines.md +0 -0
  34. {engeom-0.2.6 → engeom-0.2.8}/docs/points_vectors.md +0 -0
  35. {engeom-0.2.6 → engeom-0.2.8}/docs/surf_points.md +0 -0
  36. {engeom-0.2.6 → engeom-0.2.8}/docs/svd_basis.md +0 -0
  37. {engeom-0.2.6 → engeom-0.2.8}/engeom/.gitignore +0 -0
  38. {engeom-0.2.6 → engeom-0.2.8}/engeom/Cargo.lock +0 -0
  39. {engeom-0.2.6 → engeom-0.2.8}/engeom/Cargo.toml +0 -0
  40. {engeom-0.2.6 → engeom-0.2.8}/engeom/README.md +0 -0
  41. {engeom-0.2.6 → engeom-0.2.8}/engeom/docs/airfoils/camber.md +0 -0
  42. {engeom-0.2.6 → engeom-0.2.8}/engeom/docs/airfoils/overview.md +0 -0
  43. {engeom-0.2.6 → engeom-0.2.8}/engeom/docs/common/angles.md +0 -0
  44. {engeom-0.2.6 → engeom-0.2.8}/engeom/docs/common/core_space.md +0 -0
  45. {engeom-0.2.6 → engeom-0.2.8}/engeom/docs/common/discrete_domain.md +0 -0
  46. {engeom-0.2.6 → engeom-0.2.8}/engeom/docs/common/images/surface_point_meas.svg +0 -0
  47. {engeom-0.2.6 → engeom-0.2.8}/engeom/docs/common/svd_basis.md +0 -0
  48. {engeom-0.2.6 → engeom-0.2.8}/engeom/docs/geom2/alignment.md +0 -0
  49. {engeom-0.2.6 → engeom-0.2.8}/engeom/docs/geom2/curve.md +0 -0
  50. {engeom-0.2.6 → engeom-0.2.8}/engeom/docs/geom2/point_collections.md +0 -0
  51. {engeom-0.2.6 → engeom-0.2.8}/engeom/docs/geom2/shapes.md +0 -0
  52. {engeom-0.2.6 → engeom-0.2.8}/engeom/docs/index.md +0 -0
  53. {engeom-0.2.6 → engeom-0.2.8}/engeom/docs/javascripts/mathjax.js +0 -0
  54. {engeom-0.2.6 → engeom-0.2.8}/engeom/docs/python_rust.md +0 -0
  55. {engeom-0.2.6 → engeom-0.2.8}/engeom/mkdocs.yml +0 -0
  56. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/airfoil/camber.rs +0 -0
  57. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/airfoil/edges.rs +0 -0
  58. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/airfoil/helpers.rs +0 -0
  59. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/airfoil/inscribed_circle.rs +0 -0
  60. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/airfoil/orientation.rs +0 -0
  61. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/airfoil.rs +0 -0
  62. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/common/align.rs +0 -0
  63. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/common/angles.rs +0 -0
  64. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/common/convert_2d_3d.rs +0 -0
  65. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/common/discrete_domain.rs +0 -0
  66. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/common/indices.rs +0 -0
  67. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/common/interval.rs +0 -0
  68. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/common/kd_tree.rs +0 -0
  69. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/common/points.rs +0 -0
  70. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/common/poisson_disk.rs +0 -0
  71. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/common/surface_point.rs +0 -0
  72. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/common/svd_basis.rs +0 -0
  73. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/common/vec_f64.rs +0 -0
  74. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/common.rs +0 -0
  75. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/errors.rs +0 -0
  76. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/func1/common_functions.rs +0 -0
  77. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/func1/polynomial.rs +0 -0
  78. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/func1/series1.rs +0 -0
  79. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/func1.rs +0 -0
  80. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom2/aabb2.rs +0 -0
  81. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom2/align2/jacobian.rs +0 -0
  82. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom2/align2/points_to_curve.rs +0 -0
  83. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom2/align2/rc_params2.rs +0 -0
  84. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom2/align2.rs +0 -0
  85. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom2/angles2.rs +0 -0
  86. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom2/circle2.rs +0 -0
  87. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom2/curve2.rs +0 -0
  88. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom2/hull.rs +0 -0
  89. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom2/line2.rs +0 -0
  90. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom2/polyline2.rs +0 -0
  91. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom2.rs +0 -0
  92. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom3/align3/jacobian.rs +0 -0
  93. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom3/align3/multi_param.rs +0 -0
  94. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom3/align3/points_to_mesh.rs +0 -0
  95. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom3/align3/rotations.rs +0 -0
  96. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom3/align3.rs +0 -0
  97. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom3/curve3.rs +0 -0
  98. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom3/mesh/conformal.rs +0 -0
  99. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom3/mesh/edges.rs +0 -0
  100. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom3/mesh/faces.rs +0 -0
  101. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom3/mesh/filtering.rs +0 -0
  102. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom3/mesh/measurement.rs +0 -0
  103. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom3/mesh/patches.rs +0 -0
  104. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom3/mesh/queries.rs +0 -0
  105. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom3/mesh/sampling.rs +0 -0
  106. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom3/mesh/uv_mapping.rs +0 -0
  107. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom3/mesh.rs +0 -0
  108. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom3/plane3.rs +0 -0
  109. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom3/point_cloud.rs +0 -0
  110. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/geom3.rs +0 -0
  111. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/io.rs +0 -0
  112. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/metrology/dimension.rs +0 -0
  113. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/metrology/line_profiles.rs +0 -0
  114. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/metrology/surface_deviation.rs +0 -0
  115. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/metrology/tolerance.rs +0 -0
  116. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/metrology/tolerance_map.rs +0 -0
  117. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/metrology.rs +0 -0
  118. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/stats.rs +0 -0
  119. {engeom-0.2.6 → engeom-0.2.8}/engeom/src/utility.rs +0 -0
  120. {engeom-0.2.6 → engeom-0.2.8}/mkdocs.yml +0 -0
  121. {engeom-0.2.6 → engeom-0.2.8}/pyproject.toml +0 -0
  122. {engeom-0.2.6 → engeom-0.2.8}/python/engeom/__init__.py +0 -0
  123. {engeom-0.2.6 → engeom-0.2.8}/python/engeom/_plot/__init__.py +0 -0
  124. {engeom-0.2.6 → engeom-0.2.8}/python/engeom/_plot/common.py +0 -0
  125. {engeom-0.2.6 → engeom-0.2.8}/python/engeom/_plot/matplotlib.py +0 -0
  126. {engeom-0.2.6 → engeom-0.2.8}/python/engeom/airfoil/__init__.py +0 -0
  127. {engeom-0.2.6 → engeom-0.2.8}/python/engeom/airfoil.pyi +0 -0
  128. {engeom-0.2.6 → engeom-0.2.8}/python/engeom/align/__init__.py +0 -0
  129. {engeom-0.2.6 → engeom-0.2.8}/python/engeom/align.pyi +0 -0
  130. {engeom-0.2.6 → engeom-0.2.8}/python/engeom/engeom.pyi +0 -0
  131. {engeom-0.2.6 → engeom-0.2.8}/python/engeom/geom2/__init__.py +0 -0
  132. {engeom-0.2.6 → engeom-0.2.8}/python/engeom/geom2.pyi +0 -0
  133. {engeom-0.2.6 → engeom-0.2.8}/python/engeom/geom3/__init__.py +0 -0
  134. {engeom-0.2.6 → engeom-0.2.8}/python/engeom/metrology/__init__.py +0 -0
  135. {engeom-0.2.6 → engeom-0.2.8}/python/engeom/metrology.pyi +0 -0
  136. {engeom-0.2.6 → engeom-0.2.8}/python/engeom/plot.py +0 -0
  137. {engeom-0.2.6 → engeom-0.2.8}/python/tests/test_all.py +0 -0
  138. {engeom-0.2.6 → engeom-0.2.8}/python/tests/test_geom2_simple.py +0 -0
  139. {engeom-0.2.6 → engeom-0.2.8}/python/tests/test_geom3_simple.py +0 -0
  140. {engeom-0.2.6 → engeom-0.2.8}/src/airfoil.rs +0 -0
  141. {engeom-0.2.6 → engeom-0.2.8}/src/alignments.rs +0 -0
  142. {engeom-0.2.6 → engeom-0.2.8}/src/bounding.rs +0 -0
  143. {engeom-0.2.6 → engeom-0.2.8}/src/common.rs +0 -0
  144. {engeom-0.2.6 → engeom-0.2.8}/src/conversions.rs +0 -0
  145. {engeom-0.2.6 → engeom-0.2.8}/src/geom2.rs +0 -0
  146. {engeom-0.2.6 → engeom-0.2.8}/src/geom3.rs +0 -0
  147. {engeom-0.2.6 → engeom-0.2.8}/src/metrology.rs +0 -0
  148. {engeom-0.2.6 → engeom-0.2.8}/src/svd_basis.rs +1 -1
@@ -831,9 +831,9 @@ dependencies = [
831
831
 
832
832
  [[package]]
833
833
  name = "numpy"
834
- version = "0.23.0"
834
+ version = "0.24.0"
835
835
  source = "registry+https://github.com/rust-lang/crates.io-index"
836
- checksum = "b94caae805f998a07d33af06e6a3891e38556051b8045c615470a71590e13e78"
836
+ checksum = "a7cfbf3f0feededcaa4d289fe3079b03659e85c5b5a177f4ba6fb01ab4fb3e39"
837
837
  dependencies = [
838
838
  "libc",
839
839
  "ndarray",
@@ -841,6 +841,7 @@ dependencies = [
841
841
  "num-integer",
842
842
  "num-traits",
843
843
  "pyo3",
844
+ "pyo3-build-config",
844
845
  "rustc-hash",
845
846
  ]
846
847
 
@@ -1024,7 +1025,7 @@ dependencies = [
1024
1025
 
1025
1026
  [[package]]
1026
1027
  name = "py-engeom"
1027
- version = "0.2.6"
1028
+ version = "0.2.8"
1028
1029
  dependencies = [
1029
1030
  "engeom",
1030
1031
  "numpy",
@@ -1049,9 +1050,9 @@ dependencies = [
1049
1050
 
1050
1051
  [[package]]
1051
1052
  name = "pyo3"
1052
- version = "0.23.4"
1053
+ version = "0.24.1"
1053
1054
  source = "registry+https://github.com/rust-lang/crates.io-index"
1054
- checksum = "57fe09249128b3173d092de9523eaa75136bf7ba85e0d69eca241c7939c933cc"
1055
+ checksum = "17da310086b068fbdcefbba30aeb3721d5bb9af8db4987d6735b2183ca567229"
1055
1056
  dependencies = [
1056
1057
  "cfg-if",
1057
1058
  "indoc",
@@ -1067,9 +1068,9 @@ dependencies = [
1067
1068
 
1068
1069
  [[package]]
1069
1070
  name = "pyo3-build-config"
1070
- version = "0.23.4"
1071
+ version = "0.24.1"
1071
1072
  source = "registry+https://github.com/rust-lang/crates.io-index"
1072
- checksum = "1cd3927b5a78757a0d71aa9dff669f903b1eb64b54142a9bd9f757f8fde65fd7"
1073
+ checksum = "e27165889bd793000a098bb966adc4300c312497ea25cf7a690a9f0ac5aa5fc1"
1073
1074
  dependencies = [
1074
1075
  "once_cell",
1075
1076
  "target-lexicon",
@@ -1077,9 +1078,9 @@ dependencies = [
1077
1078
 
1078
1079
  [[package]]
1079
1080
  name = "pyo3-ffi"
1080
- version = "0.23.4"
1081
+ version = "0.24.1"
1081
1082
  source = "registry+https://github.com/rust-lang/crates.io-index"
1082
- checksum = "dab6bb2102bd8f991e7749f130a70d05dd557613e39ed2deeee8e9ca0c4d548d"
1083
+ checksum = "05280526e1dbf6b420062f3ef228b78c0c54ba94e157f5cb724a609d0f2faabc"
1083
1084
  dependencies = [
1084
1085
  "libc",
1085
1086
  "pyo3-build-config",
@@ -1087,9 +1088,9 @@ dependencies = [
1087
1088
 
1088
1089
  [[package]]
1089
1090
  name = "pyo3-macros"
1090
- version = "0.23.4"
1091
+ version = "0.24.1"
1091
1092
  source = "registry+https://github.com/rust-lang/crates.io-index"
1092
- checksum = "91871864b353fd5ffcb3f91f2f703a22a9797c91b9ab497b1acac7b07ae509c7"
1093
+ checksum = "5c3ce5686aa4d3f63359a5100c62a127c9f15e8398e5fdeb5deef1fed5cd5f44"
1093
1094
  dependencies = [
1094
1095
  "proc-macro2",
1095
1096
  "pyo3-macros-backend",
@@ -1099,9 +1100,9 @@ dependencies = [
1099
1100
 
1100
1101
  [[package]]
1101
1102
  name = "pyo3-macros-backend"
1102
- version = "0.23.4"
1103
+ version = "0.24.1"
1103
1104
  source = "registry+https://github.com/rust-lang/crates.io-index"
1104
- checksum = "43abc3b80bc20f3facd86cd3c60beed58c3e2aa26213f3cda368de39c60a27e4"
1105
+ checksum = "f4cf6faa0cbfb0ed08e89beb8103ae9724eb4750e3a78084ba4017cbe94f3855"
1105
1106
  dependencies = [
1106
1107
  "heck",
1107
1108
  "proc-macro2",
@@ -1446,9 +1447,9 @@ dependencies = [
1446
1447
 
1447
1448
  [[package]]
1448
1449
  name = "target-lexicon"
1449
- version = "0.12.16"
1450
+ version = "0.13.2"
1450
1451
  source = "registry+https://github.com/rust-lang/crates.io-index"
1451
- checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
1452
+ checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a"
1452
1453
 
1453
1454
  [[package]]
1454
1455
  name = "thiserror"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "py-engeom"
3
- version = "0.2.6"
3
+ version = "0.2.8"
4
4
  edition = "2021"
5
5
 
6
6
  # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -9,10 +9,10 @@ name = "engeom"
9
9
  crate-type = ["cdylib"]
10
10
 
11
11
  [dependencies]
12
- pyo3 = { version= "0.23.3" , features=["abi3-py38", "extension-module"]}
12
+ pyo3 = { version= "0.24.1", features=["abi3-py38", "extension-module"]}
13
13
  engeom = { path = "./engeom", features = ["stl"]}
14
14
  parry3d-f64 = {version= "0.18.0", features=["serde-serialize"]}
15
15
  parry2d-f64 = {version= "0.18.0", features=["serde-serialize"]}
16
- numpy = "0.23.0"
16
+ numpy = "0.24.0"
17
17
  rand = "0.9.0"
18
18
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: engeom
3
- Version: 0.2.6
3
+ Version: 0.2.8
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -10,6 +10,7 @@ pub mod io;
10
10
  pub mod metrology;
11
11
  pub mod stats;
12
12
  pub mod utility;
13
+ pub mod raster3;
13
14
 
14
15
  pub type Result<T> = std::result::Result<T, Box<dyn Error>>;
15
16
 
@@ -0,0 +1,60 @@
1
+ //! This module contains tools for working with 3D voxel grids.
2
+
3
+
4
+ use std::collections::HashSet;
5
+
6
+
7
+ /// This function takes a set of coordinates in a 3D grid and returns a list of clusters of
8
+ /// connected voxel coordinates.
9
+ ///
10
+ /// # Arguments
11
+ ///
12
+ /// * `indices`:
13
+ ///
14
+ /// returns: Vec<Vec<(u32, u32, u32), Global>, Global>
15
+ ///
16
+ /// # Examples
17
+ ///
18
+ /// ```
19
+ ///
20
+ /// ```
21
+ pub fn clusters_from_sparse(mut indices: HashSet<(i32, i32, i32)>) -> Vec<Vec<(i32, i32, i32)>> {
22
+ let mut results = Vec::new();
23
+
24
+ while !indices.is_empty() {
25
+ let mut working = Vec::new();
26
+ let mut to_visit = Vec::new();
27
+
28
+ to_visit.push(pop_index(&mut indices));
29
+
30
+ while !to_visit.is_empty() {
31
+ let current = to_visit.pop().unwrap();
32
+ working.push(current);
33
+
34
+ for x in -1..=1 {
35
+ for y in -1..=1 {
36
+ for z in -1..=1 {
37
+ if x == 0 && y == 0 && z == 0 {
38
+ continue;
39
+ }
40
+
41
+ let neighbor = (current.0 + x, current.1 + y, current.2 + z);
42
+ if indices.remove(&neighbor) {
43
+ to_visit.push(neighbor);
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }
49
+
50
+ results.push(working.into_iter().collect());
51
+ }
52
+
53
+ results
54
+ }
55
+
56
+ fn pop_index(indices: &mut HashSet<(i32, i32, i32)>) -> (i32, i32, i32) {
57
+ let result = indices.iter().next().unwrap().clone();
58
+ indices.remove(&result);
59
+ result
60
+ }
@@ -176,19 +176,48 @@ else:
176
176
  bold=False,
177
177
  )
178
178
 
179
- def coordinate_frame(self, iso: Iso3, size: float = 1.0):
179
+ def coordinate_frame(self, iso, size: float = 1.0, line_width=3.0, label: str | None = None,
180
+ label_size: int = 12):
180
181
  """
181
182
  Add a coordinate frame to the plotter. This will appear as three lines, with X in red, Y in green,
182
183
  and Z in blue. The length of each line is determined by the `size` parameter.
183
- :param iso: The isometry to use as the origin and orientation of the coordinate frame.
184
+ :param iso: The isometry to use as the origin and orientation of the coordinate frame. May be an `Iso3`, a
185
+ 4x4 `numpy.ndarray` that validly converts into an `Iso3`, or anything with an `as_numpy` method that
186
+ returns a valid 4x4 `numpy.ndarray`.
184
187
  :param size: The length of each line in the coordinate frame.
188
+ :param line_width: The width of the lines in the coordinate frame.
189
+ :param label: An optional label to display at the origin of the coordinate frame.
190
+ :param label_size: The size of the label text.
185
191
  """
192
+ if not isinstance(iso, Iso3):
193
+ if hasattr(iso, "as_numpy"):
194
+ iso = iso.as_numpy()
195
+
196
+ if isinstance(iso, numpy.ndarray):
197
+ if iso.shape == (4, 4):
198
+ iso = Iso3(iso)
199
+ else:
200
+ raise ValueError("Invalid shape for iso: expected (4, 4), got {iso.shape}")
201
+ else:
202
+ raise TypeError("Invalid type for iso: expected Iso3 or numpy.ndarray, got {type(iso)}")
203
+
186
204
  points = numpy.array([[0, 0, 0], [size, 0, 0], [0, size, 0], [0, 0, size]], dtype=numpy.float64)
187
205
  points = iso.transform_points(points)
188
206
 
189
- self.plotter.add_lines(points[[0, 1]], color="red", width=5.0)
190
- self.plotter.add_lines(points[[0, 2]], color="green", width=5.0)
191
- self.plotter.add_lines(points[[0, 3]], color="blue", width=5.0)
207
+ self.plotter.add_lines(points[[0, 1]], color="red", width=line_width)
208
+ self.plotter.add_lines(points[[0, 2]], color="green", width=line_width)
209
+ self.plotter.add_lines(points[[0, 3]], color="blue", width=line_width)
210
+
211
+ if label:
212
+ self.plotter.add_point_labels(
213
+ [points[0]],
214
+ [label],
215
+ show_points=False,
216
+ background_color="white",
217
+ font_family="courier",
218
+ font_size=label_size,
219
+ bold=False,
220
+ )
192
221
 
193
222
  def label(self, point: PlotCoords, text: str, **kwargs):
194
223
  """
@@ -1282,3 +1282,29 @@ class Aabb3:
1282
1282
  :return: a new AABB object with the shrunk bounds.
1283
1283
  """
1284
1284
  ...
1285
+
1286
+
1287
+ class RayBundle3:
1288
+ """
1289
+ A class representing a bundle of rays in 3D space. The rays are represented by a set of origins and directions,
1290
+ which are used to define the rays.
1291
+
1292
+ This class is used for ray tracing operations, such as intersection tests with meshes or other geometric objects.
1293
+ """
1294
+
1295
+ def __init__(self, array: NDArray[float]):
1296
+ """
1297
+ Create a ray bundle from a numpy array of shape (n, 6) containing the origins and directions of the rays.
1298
+ :param array: a numpy array of shape (n, 6) containing the origins and directions of the rays.
1299
+ """
1300
+ ...
1301
+
1302
+ def intersect_mesh(self, mesh: Mesh, iso: Iso3 | None = None, angle: float | None = None) -> NDArray[float]:
1303
+ """
1304
+
1305
+ :param angle:
1306
+ :param mesh:
1307
+ :param iso:
1308
+ :return:
1309
+ """
1310
+ ...
@@ -0,0 +1,9 @@
1
+ """
2
+ This module provides a number of classes and functions for working with 3D raster (voxel) data.
3
+ """
4
+
5
+ from ..engeom import _raster3
6
+
7
+ # Global import of all functions
8
+ for name in [n for n in dir(_raster3) if not n.startswith("_")]:
9
+ globals()[name] = getattr(_raster3, name)
@@ -0,0 +1,19 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import List
4
+
5
+ from numpy.typing import NDArray
6
+
7
+ def clusters_from_sparse(indices: NDArray[int]) -> List[NDArray[int]]:
8
+ """
9
+ Find clusters of connected voxel indices from a sparse array of voxel coordinates. The input array should be a Nx3
10
+ array of integer voxel indices. The output is a list of arrays, where each array contains the indices of a single
11
+ cluster of connected voxels.
12
+
13
+ The connectivity is defined as 26-connectivity, i.e. each voxel is connected to all neighbors with which it shares
14
+ at least one corner.
15
+
16
+ :param indices: Nx3 array of voxel indices
17
+ :return: List of arrays, each containing a Mx3 numpy array of the indices of a single cluster of connected voxels
18
+ """
19
+ ...
@@ -7,7 +7,9 @@ mod geom2;
7
7
  mod geom3;
8
8
  mod mesh;
9
9
  mod metrology;
10
+ mod raster;
10
11
  mod svd_basis;
12
+ mod ray_casting;
11
13
 
12
14
  use pyo3::prelude::*;
13
15
 
@@ -53,6 +55,9 @@ fn register_geom3(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
53
55
  child.add_class::<bounding::Aabb3>()?;
54
56
  child.add_class::<svd_basis::SvdBasis3>()?;
55
57
 
58
+ // Intersection and ray casting
59
+ child.add_class::<ray_casting::RayBundle3>()?;
60
+
56
61
  parent_module.add_submodule(&child)
57
62
  }
58
63
 
@@ -89,6 +94,14 @@ fn register_metrology_module(parent_module: &Bound<'_, PyModule>) -> PyResult<()
89
94
  parent_module.add_submodule(&child)
90
95
  }
91
96
 
97
+ fn register_raster3_module(parent_module: &Bound<'_, PyModule>) -> PyResult<()> {
98
+ let child = PyModule::new(parent_module.py(), "_raster3")?;
99
+
100
+ child.add_function(wrap_pyfunction!(raster::clusters_from_sparse, &child)?)?;
101
+
102
+ parent_module.add_submodule(&child)
103
+ }
104
+
92
105
  /// Engeom is a library for geometric operations in 2D and 3D space.
93
106
  #[pymodule(name = "engeom")]
94
107
  fn py_engeom(m: &Bound<'_, PyModule>) -> PyResult<()> {
@@ -98,6 +111,9 @@ fn py_engeom(m: &Bound<'_, PyModule>) -> PyResult<()> {
98
111
  // 3D geometry submodule
99
112
  register_geom3(m)?;
100
113
 
114
+ // 3D raster module
115
+ register_raster3_module(m)?;
116
+
101
117
  // Alignment submodule
102
118
  register_align_module(m)?;
103
119
 
@@ -161,12 +161,21 @@ impl Mesh {
161
161
  Ok(result.into_pyarray(py))
162
162
  }
163
163
 
164
- fn measure_point_deviation(&self, x: f64, y: f64, z: f64, dist_mode: DeviationMode) -> Distance3 {
164
+ fn measure_point_deviation(
165
+ &self,
166
+ x: f64,
167
+ y: f64,
168
+ z: f64,
169
+ dist_mode: DeviationMode,
170
+ ) -> Distance3 {
165
171
  let point = engeom::Point3::new(x, y, z);
166
172
  Distance3::from_inner(self.inner.measure_point_deviation(&point, dist_mode.into()))
167
173
  }
168
174
 
169
- fn boundary_first_flatten<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyArrayDyn<f64>>> {
175
+ fn boundary_first_flatten<'py>(
176
+ &self,
177
+ py: Python<'py>,
178
+ ) -> PyResult<Bound<'py, PyArrayDyn<f64>>> {
170
179
  let edges = self
171
180
  .inner
172
181
  .calc_edges()
@@ -0,0 +1,40 @@
1
+ use numpy::ndarray::{Array1, ArrayD};
2
+ use numpy::{IntoPyArray, PyArray1, PyArrayDyn, PyReadonlyArrayDyn, PyUntypedArrayMethods};
3
+ use pyo3::exceptions::PyValueError;
4
+ use pyo3::types::PyIterator;
5
+ use pyo3::{
6
+ pyclass, pyfunction, Bound, FromPyObject, IntoPyObject, IntoPyObjectExt, Py, PyAny, PyResult,
7
+ Python,
8
+ };
9
+ use std::collections::HashSet;
10
+
11
+ #[pyfunction]
12
+ pub fn clusters_from_sparse<'py>(
13
+ indices: PyReadonlyArrayDyn<'py, i32>,
14
+ ) -> PyResult<Vec<Bound<'py, PyArrayDyn<i32>>>> {
15
+ let shape = indices.shape();
16
+ if shape.len() != 2 || shape[1] != 3 {
17
+ return Err(PyValueError::new_err("Expected Nx3 array of indices"));
18
+ }
19
+
20
+ let mut working = HashSet::new();
21
+ for r in indices.as_array().rows().into_iter() {
22
+ let idx = (r[0], r[1], r[2]);
23
+ working.insert(idx);
24
+ }
25
+
26
+ let mut results = engeom::raster3::clusters_from_sparse(working);
27
+ let mut combined = Vec::new();
28
+
29
+ for result in results.drain(..) {
30
+ let mut array = ArrayD::zeros(vec![result.len(), 3]);
31
+ for (i, idx) in result.iter().enumerate() {
32
+ array[[i, 0]] = idx.0;
33
+ array[[i, 1]] = idx.1;
34
+ array[[i, 2]] = idx.2;
35
+ }
36
+ combined.push(array.into_pyarray(indices.py()));
37
+ }
38
+
39
+ Ok(combined)
40
+ }
@@ -0,0 +1,113 @@
1
+ use crate::geom3::Iso3;
2
+ use crate::mesh::Mesh;
3
+ use numpy::ndarray::{Array1, ArrayD};
4
+ use numpy::{IntoPyArray, PyArray1, PyArrayDyn, PyReadonlyArrayDyn, PyUntypedArrayMethods};
5
+ use parry3d_f64::query::{Ray as PRay3, RayCast};
6
+ use pyo3::exceptions::PyValueError;
7
+ use pyo3::types::PyIterator;
8
+ use pyo3::{
9
+ pyclass, pymethods, Bound, FromPyObject, IntoPyObject, IntoPyObjectExt, Py, PyAny, PyResult,
10
+ Python,
11
+ };
12
+
13
+ #[pyclass]
14
+ #[derive(Clone)]
15
+ pub struct RayBundle3 {
16
+ inner: Vec<PRay3>,
17
+ }
18
+
19
+ impl RayBundle3 {
20
+ pub fn from_inner(inner: Vec<PRay3>) -> Self {
21
+ Self { inner }
22
+ }
23
+
24
+ pub fn get_inner(&self) -> &Vec<PRay3> {
25
+ &self.inner
26
+ }
27
+ }
28
+
29
+ #[pymethods]
30
+ impl RayBundle3 {
31
+ #[new]
32
+ fn new<'py>(array: PyReadonlyArrayDyn<'py, f64>) -> PyResult<Self> {
33
+ let array = array.as_array();
34
+ let shape = array.shape();
35
+ if shape.len() != 2 || shape[1] != 6 {
36
+ return Err(PyValueError::new_err("Expected Nx6 array of points"));
37
+ }
38
+
39
+ let mut rays = Vec::with_capacity(shape[0]);
40
+
41
+ for row in array.rows().into_iter() {
42
+ let ray = PRay3::new(
43
+ engeom::Point3::new(row[0], row[1], row[2]),
44
+ engeom::Vector3::new(row[3], row[4], row[5]),
45
+ );
46
+ rays.push(ray);
47
+ }
48
+
49
+ Ok(Self::from_inner(rays))
50
+ }
51
+
52
+ fn __len__(&self) -> usize {
53
+ self.inner.len()
54
+ }
55
+
56
+ fn __repr__(&self) -> String {
57
+ format!("<RayBundle3 n={}>", self.inner.len())
58
+ }
59
+
60
+ #[pyo3(signature=(mesh, mesh_iso = None, angle = None))]
61
+ fn intersect_mesh<'py>(
62
+ &self,
63
+ py: Python<'py>,
64
+ mesh: &Mesh,
65
+ mesh_iso: Option<&Iso3>,
66
+ angle: Option<f64>,
67
+ ) -> PyResult<Bound<'py, PyArrayDyn<f64>>> {
68
+ let mut result = Vec::new();
69
+ let iso = if let Some(mesh_iso) = mesh_iso {
70
+ mesh_iso.get_inner()
71
+ } else {
72
+ &engeom::Iso3::identity()
73
+ };
74
+
75
+ if let Some(angle_limit) = angle {
76
+ for ray in self.inner.iter() {
77
+ if let Some(ri) =
78
+ mesh.get_inner()
79
+ .tri_mesh()
80
+ .cast_ray_and_get_normal(iso, ray, f64::MAX, false)
81
+ {
82
+ let n = ri.normal * -1.0;
83
+ if ray.dir.angle(&n) < angle_limit {
84
+ let t = ri.time_of_impact;
85
+ result.push(ray.point_at(t))
86
+ }
87
+ }
88
+ }
89
+ } else {
90
+ for ray in self.inner.iter() {
91
+ if let Some(t) = mesh
92
+ .get_inner()
93
+ .tri_mesh()
94
+ .cast_ray(iso, ray, f64::MAX, false)
95
+ {
96
+ result.push(ray.point_at(t))
97
+ }
98
+ }
99
+ }
100
+
101
+ // let result = ArrayD::from_shape_vec((result.len(), 3), result.into_iter().flat_map(|p| p.to_array()).collect())
102
+ // .map_err(|_| PyValueError::new_err("Failed to create array"))?;
103
+
104
+ let mut result_array = ArrayD::zeros(vec![result.len(), 3]);
105
+ for (i, point) in result.iter().enumerate() {
106
+ result_array[[i, 0]] = point.x;
107
+ result_array[[i, 1]] = point.y;
108
+ result_array[[i, 2]] = point.z;
109
+ }
110
+
111
+ Ok(result_array.into_pyarray(py))
112
+ }
113
+ }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,9 +1,9 @@
1
1
  use crate::conversions::{array_to_points2, array_to_points3};
2
+ use crate::geom2::{Iso2, Vector2};
2
3
  use crate::geom3::{Iso3, Vector3};
3
4
  use numpy::ndarray::Array1;
4
5
  use numpy::{IntoPyArray, PyArray1, PyReadonlyArray1, PyReadonlyArrayDyn};
5
6
  use pyo3::prelude::*;
6
- use crate::geom2::{Iso2, Vector2};
7
7
 
8
8
  #[pyclass]
9
9
  pub struct SvdBasis2 {